내가 맡은 파트
사용자 설정 게임 프레임
개발해야 할 기능
- 랜덤 입장 코드 생성
- 빠른 입장 버튼을 눌러 페이지 이동이 발생하면 DB에 방 생성
- 생성된 코드를 DB에 저장
- 코드 복사하기 버튼을 누르면 생성된 코드 복사
- 공개 방 전환
내가 생각한 로직
1. 랜덤 입장 코드 생성
커스텀 게임 페이지에 접속함과 동시에 servlet에서 대문자와 숫자로 구성된 4자리 문자열을 생성하도록 로직을 작성하였다. 미니 프로젝트인 만큼 random()함수를 사용했을 때 중복이 발생할 정도의 user 수가 많지 않을 것이라 생각했지만, 혹시 모를 경우를 대비하여 중복 확률이 훨씬 적은 SecureRandom() 함수를 사용하였다(처음써봐서 써 보고 싶기도 했다).
CustomGameServlet에 createRandomText라는 함수를 생성하였고, get요청이 발생하면 이 함수가 호출되도록 하였다. 4자리 수를 추출할 문자열을 먼저 저장한 후 범위 내에서 하나씩 선택하도록 구현하였다. 물론 선택은 secureRandom함수가 한다. (개발 중 생각난 것인데, 문자열의 길이가 짧기 때문에 아무래도 중복 확률이 많이 높을 것 같긴 하다..)
이후 이 값을 setAttribute() 함수를 통해 프론트로 전송하였고, 프론트에서 출력해 주었다.
public static StringBuilder createRandomText() { //방 입장 코드 생성 로직
String range = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //4자리 수를 추출할 문자열
SecureRandom secureRandom = new SecureRandom();
StringBuilder roomCode = new StringBuilder();
for (int i = 0; i < 4; i++) {
int rand = secureRandom.nextInt(range.length()); //문자열 범위 안에서 하나 선택
roomCode.append(range.charAt(rand));
}
return roomCode;
}
2. DB에 방 생성
초반 ERD 설계에서는 DB에 방id, 커스텀 방 여부, 참여 코드, 그리고 외래키로 회원id까지 저장하도록 하였다. 하지만, 우리 팀이 설계한 오목은 참여자가 2명인데 한 명만 저장하도록 설계되었다는 문제점을 발견하였다. 이후 ERD 수정 과정에서 회원 대 게임목록이 다 대 다임과 동시에 '게임 기록을 보여 줄 것도 아닌데 굳이 DB까지 저장해야하나'라는 의문점이 들었다. 그래서 mapping관계를 빼버리고 session값을 통해 구현하기로 하고 ERD를 수정하였다.
이후 게임 아이디는 AUTO_INCREMENT값을 주어 자동으로 1씩 증가하도록 설계하였고, DAO에서는 커스텀 방 여부를 항상 true로 저장하도록 로직을 작성해 주었고, 참여 코드만 servlet에서 넘겨준 값을 저장하도록 하였다.
public void createGame(String roomCode) { //게임 방 생성
try {
String query = "insert into GameList (is_custom, game_code) values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setBoolean(1,true);
preparedStatement.setString(2,roomCode);
preparedStatement.executeUpdate();
System.out.println("방 생성 성공");
}catch (Exception e){
e.printStackTrace();
System.out.println("방 생성 실패");
}
}
3. 생성된 코드 DB에 저장
페이지 이동이 발생하면 자동으로 방이 생성됨과 동시에 코드도 생성되게 로직을 설계하였다. 이 때, 코드가 있어야 DB에 저장할 수 있으므로 코드 생성 시점이 중요하다는 생각이 들었다. 동작 순서를 생각하니 페이지 접속 -> get요청 발생(servlet 호출) -> DAO 호출 순서였고, 코드를 생성하는 로직을 servlet에 작성하여 DAO로 넘겨 주면 되겠다는 생각이 들었다.
이후 DAO에서 매개변수로 생성된 코드 값을 받아와 DB에 저장해 주었다.
4.코드 복사 기능
코드 복사 기능을 구현해 본 적은 없지만, 굳이 서버까지 내용을 전달하지 않고 프론트에서만 처리하면 될 것 같다는 생각이 들었다.
일반적으로 대부분의 브라우저에서는 클립보드(임시로 데이터를 저장하는 데 사용되는 시스템의 일부)에 복사를 하는 형태이고, 이 내용은 브라우저가 닫힐 때까지 유지되기 때문이다.
처음에는 document.execCommand('copy') 명령어만 사용해 주면 코드가 동작할 것이라 생각하였다. 이 명령어는 '현재 문서나 선택된 텍스트를 클립보드에 복사'하는 명령어이다. 복사 버튼에서 클릭 이벤트가 발생하면 만들어둔 change() 함수가 호출되도록 코드를 작성하였기 때문에 당연히 현재 요소가 선택될 것이라고 생각하였다.
<input type="button" onclick="copy()" class="codeBox-buttons-copy" value="코드복사"/>
하지만, 사용자가 코드를 드래그 해서 복사 버튼을 누르는 것이 아니라 그냥 버튼만 누르는 것이기 때문에 프론트 로직에서 텍스트 범위를 선택 해 주어야 복사가 된다.
function copy() {
var roomCode = document.getElementById('roomCode2'); //id가 roomCode인 값을 가져와 roomCode에 대입
var range = document.createRange(); //복사할 텍스트의 범위 지정
range.selectNode(roomCode); //roomCode 요소의 내용 선택
//window.getSelection().removeAllRanges(); //범위 제거
window.getSelection().addRange(range); //위에서 만든 range를 현재 선택에 추가
document.execCommand('copy'); //클립보드에 복사
alert('방 코드가 복사되었습니다.');
}
5. 공개 방 전환
처음에는 단순히 DB에서 현재 방의 id값을 조회해 is_custom 값을 false로 바꿔줄 생각이었다. 하지만, 현재 방의 id 값을 조회하는 로직이 비효율적이라는 생각이 들었다. user 두 명은 세션 값으로 조회할 것이고, 방 id값은 DB에서 자동으로 만들어 주기 때문에 id를 한번 더 조회하는 로직을 추가하는 것 보단 이미 사용하고 있는 game_code 값으로 조회하는 것이 더 효율적이라는 생각이 들었다.
그러나, game_code를 프론트에서 받아오는 과정이 생각만큼 간단하지는 않았다. 서버와 통신을 하려면 ajax문이 필요하기 때문이다. 나는 ajax를 무조건 써야 하는지는 몰랐는데, 서버와 프론트가 통신을 하려면 꼭 필요하다고 한다.
public void changeIsCustom(String roomCode){ //현재 방의 roomCode를 기반으로 그 방의 is_custom 값 변경
try{
String query = "update gamelist set is_custom=? where game_code=?";
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setBoolean(1,false);
preparedStatement.setString(2,roomCode);
preparedStatement.executeUpdate();
System.out.println("방 전환 성공");
}catch (Exception e){
e.printStackTrace();
System.out.println("방 전환 실패");
}
}
이후, 단순히 game_code 값을 가지고 검색을 수행하는 것 보다 is_custom값이 true인 방을 먼저 검색하고 game_code로 검색하는 것이 더 빠를 것 같다는 생각이 들어, mysql에서 테스트를 해 보았다.
이 때, 두 개의 테스트를 동일한 조건 하에 검색하기 위하여 쿼리문 사이에 select * from gamelist;를 넣어 메모리에 전부 로드되도록 해 주었다.
위의 두 쿼리문의 실행 차이는 나지 않았다. 왜냐면 목록의 개수가 너무 적었기 때문이다. 그래서 PROFILES 기능을 사용하여 소숫점 아래 자리까지 실행 시간을 측정해 보았다.
차이는 매우 컸다. 향상된 쿼리문이 기존 쿼리문보다 약 2.34배 더 빠르기 때문이다.
'웹소켓을 활용한 실시간 오목 게임 [현오목]' 카테고리의 다른 글
프로젝트 소개 및 회고, 강사님 피드백 (2) | 2025.01.09 |
---|---|
게임 결과 저장 로직 개발 (1) | 2025.01.09 |
웹 소켓 개발 (0) | 2025.01.09 |
개발 시작 전 회의 (0) | 2025.01.09 |