Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 

Repository files navigation

ChatJava

1차 개발기간 : 2023.10.20 ~ 2023.10.26 --> 리팩토링 및 기능 추가 예정


프로젝트 소개 :

  • ChatJava는 소켓프로그래밍, 디비를 활용한 프로그램입니다.

화면 구성 📺

회원가입

로그인 회원가입
회원가입- 우편번호주소 회원가입- 회원가입완료
회원가입- 필드검사 회원가입- (패스워드/아이디) 확인

채팅

채팅페이지 전송기능
회원탈퇴 닉네임 변경
닉네임변경2 글자색

프로토콜 설계

프로토콜 전송기능
Login 100
MESSAGE 200
CHANGE 202
DELETE 210

주요 기능

⭐️ 로그인

  • 회원가입, 로그인 2개의 기능 구현
  • 회원가입 시 디비에 저장 되어 있는 ID/Paswword 가져와서 정보가 맞으면 로그인 성공, 틀리면 로그인 실패

⭐️ 회원가입

  • 모든 필드을 충족 후 회원가입 가능 구현(유효성 검사)
  • 회원가입 시 ID/Password/Nickname 디비 저장.

⭐️ 채팅창

  • 전송/Enter 누르면 사용자에게 메세지 전송 기능 제공
  • 닉네임변경 기능을 이용하여 해당 사용자 닉네임 변경 기능 제공
  • 글자색 버튼 기능으로 채팅창 내 글자색 변경 가능
  • 회원 탈퇴을 이용하여 저장 되어 있는 회원정보 삭제 기능 제공
  • 추후 회원 리스트 CRUD 추가 및 업로드 예정

시작 가이드

Requirements

Installation

$ git clone https://github.com/Ohsaam/ChatJava.git
$ cd ChatJava

Stacks 🐈

Environment

  • Visual Studio Code
  • Git
  • Github

Development

아키텍쳐

📦com
 ┣ 📂database
 ┃ ┣ 📂zipcodeview
 ┃ ┃ ┗ 📜DBConnectionMgr.java
 ┃ ┣ 📜DBConnection.java
 ┃ ┣ 📜MemberDao.java
 ┃ ┗ 📜MemberDTO.java
 ┣ 📂soket
 ┃ ┣ 📂client
 ┃ ┃ ┣ 📜SocketClient.java
 ┃ ┃ ┗ 📜SocketClientThread.java
 ┃ ┗ 📂server
 ┃ ┃ ┣ 📜Protocol.java
 ┃ ┃ ┣ 📜SocketServer.java
 ┃ ┃ ┗ 📜SocketServerThread.java
 ┗ 📂ui
 ┃ ┣ 📜LoginForm.java
 ┃ ┣ 📜MemberListFrame.java
 ┃ ┣ 📜MemberShipView.java
 ┃ ┣ 📜Sample.java
 ┃ ┗ 📜ZipCodeView.java

이슈관리 & 회고

로그인 관련 이슈

image

  • 설계 방향) 아이디/패스워드/대화명/성명 + 입력 후 등록 누르면 -> 해당 디비에 저장하여 로그인 버튼 눌렀을 때 그 값을 꺼내와서 "로그인이 성공되었습니다." 라고 설계했다.

  • 회원가입 창에서 MemberShipView을 이용하여 값을 받아옴 -> 처음엔 무작정 클래스 생성자 안 this를 통해서 값을 getText() 하려고 했는데, 처음 설계 방향 자체를 잘못 잡았다. -> 그래서 테이블 스키마 자체를 다시 설계하여 디비와 연동했다. 그리고 해당 쿼리문을 다시 짜서 -> select 문으로 nickname을 찾고 -> setString 자체를 username/password로 진행했다. (findNickName 메소드 구현) 해서 회원가입 창에서 값을 읽어들이는 부분을 해결했다. ( findNickName 메소드는 소켓통신에 key값으로 닉네임을 넘기는 디비 메소드)

회원가입 이슈

패스워드 입력시 : 값들이 입력되는데 캡슐화로 ( *** ) 바꾼다.

  • 처음엔 변수를 2개를 써서 -> 스트링으로 읽은 다음 -> replace해서 변환하려고 했다.(**) 하지만 이 부분은 이렇게 하는 것 보단 구글링을 통해 JPasswordField 이용하여 구현완료

아이디 / 패스워드 / 닉네임 디비연동

image

java.sql.SQLSyntaxErrorException: ORA-00942: 테이블 또는 뷰가 존재하지 않습니다
  • 수정 했더니 해당 에러가 발생했고, 그렇다면 테이블 또는 뷰를 생성해서 연동시켜야했다. 처음엔 오라클 연동을 한 번 해봐서 자신은 없었지만 구선생을 통해 쿼리문을 짰다.

  • 처음엔 단순히 인스턴스화를 통한 전역변수 이용을 하여 구현 하려고 했다. 처음엔 DB를 설계를 하지 않았는데, 아이디,패스워드, 회원정보를 저장 해야 된다는 필요성을 느껴 toad에서 테이블 등록(member) 후 아이디와 패스워드, 닉네임(회원가입창)에서 받아와서 Dao 클래스에 (Save) 메소드에서 해당 내용을 저장하는 디비를 완성했다. (Save 메소드는 로그인을 하기 위한 회원내용 저장)

소켓통신 이슈

소켓통신 연동 이슈

  • 지금까지 연동하여 로그인을 했을 때 채팅창이 열리는 채팅프로그램을 구현 하고 싶었다.(요구사항)

  • 하지만 지금까지 설계한 부분으로는 클래스 설계를 다시 해야 되는 상황이다. 우선 필자가 생각한 설계 방향은 우선 아이디와 비밀번호를 입력하면, 디비에 넘기는데 여기서 추가로 해야 될 부분은 닉네임을 추출하여 소켓과 연동시키는 설계방향으로 진행했다.

  • 이렇게 구현을 먼저하고 설계를 다시 하니 일을 두 번 하는 번거로움이 발생했다. 이 부분만 줄였어도 더 많은 기능을 구현 했을텐데 안타깝다. 다시 한 번 설계의 중요성을 느꼈고, 앞으론 Class Diagram을 이용해 설계를 먼저 해야겠다.

소켓통신 구현 이슈

  • 우선 싱글스레드가 아닌, 멀티스레드로 구현 했기 떄문에 회원 탈퇴 기능을 추가 했을 때, 2개의 채팅창이 있을 때, 한쪽에서 탈퇴하면 바로 Update 하는 메소드를 설계했다.

  • 처음엔 Client/ClientThread만 수정하고 -> dtm.getValutAt() / dtm.setValueAt 이벤트 처리 메소드 설계 및 디비에서 삭제하는 메소드 설계 -> 이런 논리로 회원탈퇴를 눌렀을 때 해당 로그인한 아이디의 내용을 디비에서 삭제하고, 해당 row을 줄이는그리고 로그인 시 해당 회원정보는 삭제 되었기 때문에 다시 접속해야 되는 방향으로 설계했다. 하지만 시점의 문제가 발생했다.

  • A라는 회원이 먼저 로그인하고 B라는 회원이 로그인 했을 때, 먼저 들어온 A를 회원 탈퇴하면 그 내용을 B에 반영하지 못했다. 해당 문제를 겪게 되면서 클래스 시점에 대한 고찰을 다시 한 번 하게 된 것 같다. 클라이언트가 소켓을 서버에 전송하게 되면 (oos)서버에서 그 내용을 읽고(ois) 다시 전송하게 되는데 이 전송과정에 대한 구현이 빠져서 이슈가 발생한 것이였다.

  • 이 부분을 해결하기 위해서 필자는 LogoutRequest,removeList,sendUserList 3개의 메소드를 구현했다.

LogoutRequest

   public void LogoutRequest(String nickName) {
       try {
           String message = nickName + "님이 퇴장하였습니다.";
           broadCasting(210 + "#" + nickName + "#" + message);
           // 사용자 목록 업데이트
           removeList(nickName);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

// 사용자 목록에서 사용자 제거
   public void removeList(String nickName) {
       for (SocketServerThread sst : ss.globalList) {
           if (sst != null && sst.chatName.equals(nickName)) {
               ss.globalList.remove(sst);
               break;
           }
       }
   }

   
   // 사용자 목록을 클라이언트에게 전송
   //서버에 접속할 때 또는 사용자 목록을 요청할 때 사용
   public void sendUserList(SocketServerThread cthread) throws IOException {
       StringBuilder userListMessage = new StringBuilder("100#"); // 목록 메시지를 만들기 위한 문자열 빌더
       for (SocketServerThread sst : ss.globalList) {
           userListMessage.append(sst.chatName).append("#");
       }
       /**
        * 루프 내에서, 각 클라이언트 스레드 sst의 chatName을 가져와서 userListMessage에 # 문자와 함께 추가한다.
        */
       cthread.send(userListMessage.toString());
       System.out.println("회원탈퇴");
   }
  • 이 부분을 구현하면서 가장 어려웠던 부분은 globallist를 이용해야 된다는 건 인지하고 있었지만, 조건을 어떤식으로 걸어야 될지 한참 고민 한 것 같다. 해당 메소드를 구현하면서 이슈는 해결했고, 이를 기점으로 1차 프로젝트는 마무리했다.

포트연결 및 닉네임 연동 오류

image image

  • 소켓 연동은 성공 했으나 닉네임을 받지 못하는 해당 오류가 발생했다.
java.util.NoSuchElementException
   at java.base/java.util.StringTokenizer.nextToken(StringTokenizer.java:347)
  • server/ serverThead /clientThread 모두 닉네임을 연동 시켜야 된다고 판단했고 그 문제가 되는 클래스 내부 메소드 로그를 찍어서 문제 되는 부분을 확인했다. ->

  • 이 부분에서 처음 생각한 방향은 디비를 사용하지 않고 인스턴스화, 클래스(this)를 통한 방향으로 진행을 설계하여 세션의 개념처럼 nickname을 싱글톤으로 구현했다. 하지만 이 부분에서 필자가 간과한 부분이 있다. 소켓통신 프로그램은 멀티스레드인데 싱글톤으로 따로 만들었다니. 해당 부분을 진행하면서 스레드에 대한 공부를 다시 하게 됐다. 블로그 참고

troubleshooting

  • 해당 오류 관련된 부분은 필자 블로그란에서 진행하고 있다.

About

JFrame + Socket Programming

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages