기말 프로젝트를 어찌어찌 잘 넘겼다. (이미 종강했다)
진짜 너무 힘들었다... 팀 세명 중이 둘이서만 코드를 쳐야 했고 소켓 통신을 처음 해보면서 처음 구조부터 엉망으로 짜기 시작해서 나중엔 하드코딩으로 디비를 이용해 메시지를 찍어내야 했다...ㅋㅋ
종강하고 나서 여유롭게 일주일을 쉬고 나니 이 프로젝트 전의 나와 후의 나를 돌아보니 많은 것이 바뀌어 있었다.
예전에는 잘 못 느꼈던 성취감을 이번에는 확실하게 맛봤다.
그래서 앞으로의 프젝이 참 기대가 된다.
메인 프레임은 다른 팀원분이 해주셨다.
그분이 프런트단을 다 해주셔서 나는 백엔드에 전념할 수 있었다.
나중엔 AWS를 사용해서 디비를 만들어 사용했다.
기존에 21-1 방학 프로젝트를 할 때 사용하던 RDS 서버가 있었는데 그걸 활용했다.
나중에 생각해보니 굳이 디비를 서버에 올리지 않아도 됐을 거 같은데... JDBC로 하드 코딩해서 어떻게든 되었을 거 같다.
이 화면도 다른 팀원분이 프런트를 맡아서 해주셨다.
기능은 내가 만들어둔 메서드가 있어서 그대로 끌어서 사용했으면 되는데 그 과정 중에 merge conflict가 발생해 브랜치를 삭제해야 하는 상황이 생겼다.
그분은 협업이 처음이었고, 나는 경험이 있었지만 내가 주도하면서 하는 프로젝트는 처음이었다.
(그동안은 초보자 수준의 프로젝트를 해왔었고, 서로 친한 친구사이어서 알아서 코드를 공유하고 깃을 제대로 쓰지도 않았다.)
그래서 서로 합이 잘 맞지 않아서 조심히 행동했었던 거 같다.
지금 생각해보면 그렇게 행동한 것이 정말 잘한 일이라 생각한다.
프로젝트가 끝나고 보니 그전에 힘들고 서운했던 마음은 없어지고 뿌듯하고 한층 가까워진 기분이 들었다.
난 프로젝트가 끝나고 코딩이 더 재미있어졌다.
채팅 관련한 기능은 내가 맡았다.
채팅에 사용되는 클라이언트 소켓과 서버 소켓을 만들고 AWS에서 RDS 서버를 사용해서 메시지를 각각의 방에 맞춰서 넣었다.
그 과정 중엔 나와 막역한 사이인 선배님이 쿼리문 짜는 것을 도와주었다.
(클라이언트에서 보낸 메시지를 서버에서도 표시할 수 있게 하는 기능을 도와주셨지만 안타깝게도 내가 처음에 짠 구조 때문에 하드코딩이 먹히질 않았다. 역시 나무를 베기 전엔 도끼부터 갈아야 한다...)
그에 비해 데이터베이스는 참 이쁘게 짰다. 사용한 프로그램은 exerd이다.
package DB;
import java.net.InetAddress;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;
import dto.Member;
public class Login {
Scanner sc = new Scanner(System.in);
/**
* 회원가입 기능
*/
public void SignIn() {
try {
InetAddress local = InetAddress.getLocalHost();
String ip = local.getHostAddress();
DBUtil util = new DBUtil();
Connection conn = null;
conn = util.open();
PreparedStatement ps = null;
System.out.print("학번을 입력하세요 : ");
int studentNo = sc.nextInt();
System.out.print("\n사용할 아이디를 입력하세요 : ");
String id = sc.next();
System.out.print("\n사용할 비밀번호를 입력하세요 : ");
String password = sc.next();
System.out.print("\n본명을 입력하세요 : ");
String name = sc.next();
System.out.print("\n학년을 입력하세요 : ");
int grade = sc.nextInt();
System.out.print("\n전공을 입력하세요 : ");
String subject = sc.next();
String job = "";
if (grade > 4 || grade < 1) {
job = "학생";
}else {
System.out.print("\n현재 직업을 입력하세요 : ");
job = sc.next();
}
String sql = "INSERT INTO MEMBER(STUDENTNO, ID, PASSWORD, NAME, GRADE, SUBJECT, IP, JOB)\r\n"
+ "VALUES ("+studentNo+", '"+id+"', '"+password+"', '"+name+"', "+grade+", '"+subject+"', '"+ip+"', '"+job+"');";
ps = conn.prepareStatement(sql);
ResultSet rs = null;
rs = ps.executeQuery();
util.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 로그인 기능
* @param member
*/
public Member memberLogin(Member member) {
Member loginMember = new Member();
try {
System.out.println(member.getId());
System.out.println(member.getPassword());
DBUtil util = new DBUtil();
Connection conn = null;
conn = util.open();
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "SELECT STUDENTNO, ID, NAME, GRADE, SUBJECT, JOB FROM MEMBER WHERE ID = ? AND PASSWORD = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, member.getId());
ps.setString(2, member.getPassword());
rs = ps.executeQuery();
while(rs.next()) {
loginMember.setStudentNo(rs.getInt("STUDENTNO"));
loginMember.setId(rs.getString("ID"));
loginMember.setName(rs.getString("NAME"));
loginMember.setGrade(rs.getInt("GRADE"));
loginMember.setSubject(rs.getString("SUBJECT"));
loginMember.setJob(rs.getString("JOB"));
}
util.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("123123123" + loginMember.getName());
return loginMember;
}
}
회원가입 기능과 로그인 기능이다.
JDBC를 이용해서 디비 상에 데이터를 추가하는 과정이다.
내가 기능을 만들어 뒀으니 여기서 System.out.println은 어차피 콘솔 상에만 보이니까 신경 쓰지 말고 메서드를 가져다 쓰면 된다는 말을 했었는데 다른 팀원분이 그냥 직접 만드시고 쿼리문만 복사해서 가져가셨다...
(나중에 쿼리문이 안된다고 말하시길래 다시 다 확인하고 업로드했었다. 근데 다른 팀원분이 커밋을 올리면서 이미 컨플릭트가 난 상황에서 올리려 하니 안 올라가니까 내 브랜치에 올려버리셨다... 그래서 결국 한 브랜치는 날려버리고 다른 브랜치는 롤백을 해서 기존의 코드를 삭제해서 맞췄다.)
난 처음에는 정말 정말 화가 났었다.
근데 결국엔 풀 리퀘스트 방식을 채택하지 않은 내 판단 미스라는 것이 잘못이었다. 그냥 그런 관리 없이도 할 수 있다 생각했는데 그건 잘못된 생각이었다. 결국 코드 리뷰는 필수적이라는 걸 다시 한번 느꼈다.
try {
DBUtil util = new DBUtil();
Connection conn = null;
conn = util.open();
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "SELECT ROOMNO FROM (SELECT LISTAGG(STUDENTNO, ',') WITHIN GROUP(ORDER BY ROOMNO, STUDENTNO) AS CLIENT, ROOMNO FROM PARTICIPANTS GROUP BY ROOMNO) WHERE CLIENT = ?|| ',' ||?";
ps = conn.prepareStatement(sql);
ps.setInt(1, client.getStudentNo() > member.getStudentNo() ? member.getStudentNo() : client.getStudentNo());
ps.setInt(2, client.getStudentNo() < member.getStudentNo() ? member.getStudentNo() : client.getStudentNo());
rs = ps.executeQuery();
int roomNo = 0;
if (rs.next()) {
roomNo = rs.getInt("ROOMNO");
}
System.out.println("1 : " + roomNo);
if (roomNo == 0) {
sql = "INSERT INTO CHATTING_ROOM (ROOMNO, ROOMNM) VALUES (CHATTING_ROOM_SQ.NEXTVAL, '" + title + "')";
ps = conn.prepareStatement(sql);
ps.executeQuery();
sql = "INSERT INTO PARTICIPANTS VALUES(PARTICIPANTS_SQ.NEXTVAL, ?, CHATTING_ROOM_SQ.CURRVAL)";
ps = conn.prepareStatement(sql);
ps.setInt(1, loginMember.getStudentNo());
ps.executeQuery();
ps = conn.prepareStatement(sql);
ps.setInt(1, clientMember.getStudentNo());
ps.executeQuery();
} else {
sql = "SELECT NAME, CONTENT FROM CHATTING C INNER JOIN MEMBER M ON C.STUDENTNO = M.STUDENTNO WHERE C.ROOMNO = ? ORDER BY CHATINDEX";
ps = conn.prepareStatement(sql);
ps.setInt(1, roomNo);
rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString("NAME");
String content = rs.getString("CONTENT");
String message = name + " : " + content + "\n";
chatGround.append(message);
}
}
util.close();
} catch (Exception e) {
e.printStackTrace();
}
개인적으로 참 잘 짰다고 생각하는 코드이다.
LISTAGG를 사용해서 기존에 대화했었던 방을 그대로 다시 들어갈 수 있게 하는 기능이었다.
한마디로 카톡에서 방을 한번 만들면 그 방에 메시지가 그대로 남아있는 기능을 구현했다고 보면 된다.
(이걸 발표날에 어필했어야 하는데, 발표자는 따로 있어서 나는 내가 시연만 하는 줄 알았는데 시연과 발표도 같이 하게 되면서 준비를 하나도 못했었다. 다음엔 설마 하겠어하지 말고 좀 준비하자..ㅋㅋ)
@Override
public void keyPressed(KeyEvent e) {
try {
if (e.getKeyCode() == e.VK_ENTER) {
InetAddress local = InetAddress.getLocalHost();
String ip = local.getHostAddress();
// String ip = "192.168.219.102";
socket = new Socket(ip, 1593);
String data = chatInput.getText();
ObjectOutputStream osw = new ObjectOutputStream(socket.getOutputStream());
// List<HashMap<Member, String>> message = new ArrayList<HashMap<Member, String>>();
List<MessageVO> message = new ArrayList<MessageVO>();
MessageVO messagevo = new MessageVO();
messagevo.setMember(member);
messagevo.setText(data);
// HashMap<Member, String> text = new HashMap<Member, String>();
// text.put(member, data);
// message.add(text);
message.add(messagevo);
osw.writeObject(message);
chatInput.setText("");
chatGround.append(messagevo.getMember().getName() + " : " + messagevo.getText() + "\n");
// chatGround.append(member.getName() + " : " + message.get(0).get(member) + "\n");
DBUtil util = new DBUtil();
Connection conn = null;
conn = util.open();
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "SELECT ROOMNO FROM (SELECT LISTAGG(STUDENTNO, ',') WITHIN GROUP(ORDER BY ROOMNO, STUDENTNO) AS CLIENT, ROOMNO FROM PARTICIPANTS GROUP BY ROOMNO) WHERE CLIENT = ? || ',' ||?";
ps = conn.prepareStatement(sql);
ps.setInt(1,
client.getStudentNo() > member.getStudentNo() ? member.getStudentNo() : client.getStudentNo());
ps.setInt(2,
client.getStudentNo() < member.getStudentNo() ? member.getStudentNo() : client.getStudentNo());
rs = ps.executeQuery();
int roomNo = 0;
if (rs.next()) {
roomNo = rs.getInt("ROOMNO");
}
System.out.println("2 : " + roomNo);
sql = "INSERT INTO CHATTING VALUES(CHATTING_SQ.NEXTVAL, ?, ?, ?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, member.getStudentNo());
ps.setString(2, data);
ps.setInt(3, roomNo);
rs = ps.executeQuery();
sql = "SELECT NAME, CONTENT FROM CHATTING C INNER JOIN MEMBER M ON C.STUDENTNO = M.STUDENTNO WHERE C.ROOMNO = ? ORDER BY CHATINDEX";
ps = conn.prepareStatement(sql);
ps.setInt(1, roomNo);
rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString("NAME");
String content = rs.getString("CONTENT");
String messages = name + " : " + content + "\n";
chatGround.append(messages);
}
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
얼마나 급하게 마감을 쳤으면 아직도 지우지 못한 테스트 코드가 남아있을까?
아마 깃 허브에서 코드를 보면 알겠지만, 프로젝트가 정말 지저분하다.
얼기설기 이어 붙여서 키메라를 만든 거 같다... 돌아가긴 하는데 보기엔 안 좋은...
다음에는 좀 더 기획단계에서부터 세세하게 잡고 들어가야겠다.
어떤 기능을 언제까지 한다는 데드라인만 정하는 게 아니라 어떤 기능을 만들 때 어떤 구조로 어떤 데이터를 넣을 건지, 나중에 연결할 때는 어떻게 돌아가게끔 할 것인지 말이다.
여기서부터 이제 코드 몽키 시간이다.
@Override
public void keyReleased(KeyEvent e) {
try {
if (e.getKeyCode() == e.VK_ENTER) {
// InetAddress local = InetAddress.getLocalHost();
// String ip = local.getHostAddress();
//// String ip = "192.168.219.102";
// socket = new Socket(ip, 1593);
String data = chatInput.getText();
// ObjectOutputStream osw = new ObjectOutputStream(socket.getOutputStream());
//// List<HashMap<Member, String>> message = new ArrayList<HashMap<Member, String>>();
// List<MessageVO> message = new ArrayList<MessageVO>();
// MessageVO messagevo = new MessageVO();
// messagevo.setMember(member);
// messagevo.setText(data);
//// HashMap<Member, String> text = new HashMap<Member, String>();
//// text.put(member, data);
//// message.add(text);
// message.add(messagevo);
// osw.writeObject(message);
// chatInput.setText("");
// chatGround.append(messagevo.getMember().getName() + " : " + messagevo.getText() + "\n");
//// chatGround.append(member.getName() + " : " + message.get(0).get(member) + "\n");
DBUtil util = new DBUtil();
Connection conn = null;
conn = util.open();
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "SELECT ROOMNO FROM (SELECT LISTAGG(STUDENTNO, ',') WITHIN GROUP(ORDER BY ROOMNO, STUDENTNO) AS CLIENT, ROOMNO FROM PARTICIPANTS GROUP BY ROOMNO) WHERE CLIENT = ? || ',' ||?";
ps = conn.prepareStatement(sql);
ps.setInt(1,
client.getStudentNo() > member.getStudentNo() ? member.getStudentNo() : client.getStudentNo());
ps.setInt(2,
client.getStudentNo() < member.getStudentNo() ? member.getStudentNo() : client.getStudentNo());
rs = ps.executeQuery();
int roomNo = 0;
if (rs.next()) {
roomNo = rs.getInt("ROOMNO");
}
System.out.println("2 : " + roomNo);
sql = "INSERT INTO CHATTING VALUES(CHATTING_SQ.NEXTVAL, ?, ?, ?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, member.getStudentNo());
ps.setString(2, data);
ps.setInt(3, roomNo);
rs = ps.executeQuery();
sql = "SELECT NAME, CONTENT FROM CHATTING C INNER JOIN MEMBER M ON C.STUDENTNO = M.STUDENTNO WHERE C.ROOMNO = ? ORDER BY CHATINDEX";
ps = conn.prepareStatement(sql);
ps.setInt(1, roomNo);
rs = ps.executeQuery();
while (rs.next()) {
String name = rs.getString("NAME");
String content = rs.getString("CONTENT");
String messages = name + " : " + content + "\n";
chatGround.append(messages);
}
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
나는 이 프로젝트를 하면서 정말 큰 문제에 봉착했다.
서버에서 클라이언트로 메시지를 보내면 바로 채팅창에 올라오는데 그 반대로 클라이언트에서 서버로 메시지를 보내면 서버의 채팅창은 아무 반응이 없는 것이다.
사실 이 문제는 소켓을 하나 더 열어서 클라이언트끼리 통신을 하게 만들면 되는 일이었는데 그 당시의 나는 클라이언트 소켓을 여러 개를 열 수 있다는 사실을 몰랐다.
그래서 이 코드가 탄생했다.
디비에서 채팅 내역을 긁어오게 만든 것이다.
그것도 스레드를 사용한 것도 아닌.. 진짜 코드 몽키 같은 짓을 말이다.
정말 부끄럽지만 다신 이런 짓을 안 하려고 여기 기록한다...
다음부터는 진짜 스레드를 쓰자..
곧 2021년이 지나간다.
나는 참 운이 좋은 사람이다.
나를 도와주는 사람들 덕분에 올해는 정말 많은 성장을 이뤄냈다.
성장하면서 나의 2022년 목표가 많이 생겼다.
내년에도 힘내자!
'Java > Java Swing' 카테고리의 다른 글
Java Swing 첫 개인 프로젝트 - Swing으로 물고기키우기 게임 만들기 (0) | 2021.09.28 |
---|