1차 개발기간 : 2023.10.20 ~ 2023.10.26 --> 리팩토링 및 기능 추가 예정
로그인 | 회원가입 |
---|---|
회원가입- 우편번호주소 | 회원가입- 회원가입완료 |
회원가입- 필드검사 | 회원가입- (패스워드/아이디) 확인 |
채팅페이지 | 전송기능 |
---|---|
회원탈퇴 | 닉네임 변경 |
닉네임변경2 | 글자색 |
프로토콜 | 전송기능 |
---|---|
Login | 100 |
MESSAGE | 200 |
CHANGE | 202 |
DELETE | 210 |
- 회원가입, 로그인 2개의 기능 구현
- 회원가입 시 디비에 저장 되어 있는 ID/Paswword 가져와서 정보가 맞으면 로그인 성공, 틀리면 로그인 실패
- 모든 필드을 충족 후 회원가입 가능 구현(유효성 검사)
- 회원가입 시 ID/Password/Nickname 디비 저장.
- 전송/Enter 누르면 사용자에게 메세지 전송 기능 제공
- 닉네임변경 기능을 이용하여 해당 사용자 닉네임 변경 기능 제공
- 글자색 버튼 기능으로 채팅창 내 글자색 변경 가능
- 회원 탈퇴을 이용하여 저장 되어 있는 회원정보 삭제 기능 제공
- 추후 회원 리스트 CRUD 추가 및 업로드 예정
$ git clone https://github.com/Ohsaam/ChatJava.git
$ cd ChatJava
📦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
-
설계 방향) 아이디/패스워드/대화명/성명 + 입력 후 등록 누르면 -> 해당 디비에 저장하여 로그인 버튼 눌렀을 때 그 값을 꺼내와서 "로그인이 성공되었습니다." 라고 설계했다.
-
회원가입 창에서 MemberShipView을 이용하여 값을 받아옴 -> 처음엔 무작정 클래스 생성자 안 this를 통해서 값을 getText() 하려고 했는데, 처음 설계 방향 자체를 잘못 잡았다. -> 그래서 테이블 스키마 자체를 다시 설계하여 디비와 연동했다. 그리고 해당 쿼리문을 다시 짜서 -> select 문으로 nickname을 찾고 -> setString 자체를 username/password로 진행했다. (findNickName 메소드 구현) 해서 회원가입 창에서 값을 읽어들이는 부분을 해결했다. ( findNickName 메소드는 소켓통신에 key값으로 닉네임을 넘기는 디비 메소드)
- 처음엔 변수를 2개를 써서 -> 스트링으로 읽은 다음 -> replace해서 변환하려고 했다.(**) 하지만 이 부분은 이렇게 하는 것 보단 구글링을 통해 JPasswordField 이용하여 구현완료
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차 프로젝트는 마무리했다.
- 소켓 연동은 성공 했으나 닉네임을 받지 못하는 해당 오류가 발생했다.
java.util.NoSuchElementException
at java.base/java.util.StringTokenizer.nextToken(StringTokenizer.java:347)
-
server/ serverThead /clientThread 모두 닉네임을 연동 시켜야 된다고 판단했고 그 문제가 되는 클래스 내부 메소드 로그를 찍어서 문제 되는 부분을 확인했다. ->
-
이 부분에서 처음 생각한 방향은 디비를 사용하지 않고 인스턴스화, 클래스(this)를 통한 방향으로 진행을 설계하여 세션의 개념처럼 nickname을 싱글톤으로 구현했다. 하지만 이 부분에서 필자가 간과한 부분이 있다. 소켓통신 프로그램은 멀티스레드인데 싱글톤으로 따로 만들었다니. 해당 부분을 진행하면서 스레드에 대한 공부를 다시 하게 됐다. 블로그 참고
- 해당 오류 관련된 부분은 필자 블로그란에서 진행하고 있다.