1. 어려웠던 부분 :
- 오늘 백엔드팀은 프로젝트에 관하여 급하게 처리할 사항이 없어서 코드리뷰의 시간을 가졌다. S3는 이전 프로젝트에서 사용해 봤기 때문에 대략적으로 흐름을 알고 있었지만, 웹소켓과 웹알티씨 관련된 부분은 코드를 보아도 이해가 잘 되지 않았다.
- 그래도 담당하신 팀원분이 차근히 설명을 해주셔서 대략적으로 이게 어떻게 돌아가는지는 알 것 같았다.
2. 느낀 점 :
- 뷰작업에 한창인 프론트엔드와 달리 백엔드는 비교적 일찍 작업이 마무리된다. 덕분에 내가 구현하지 않은 부분을 더 공부할 수 있다. 코드를 많이 봐야 한다고 들었는데, 코드를 보면 볼수록 이 컴퓨터의 세계는 정말 신선하다. 알파벳 몇자로 애플리케이션이 만들어지는 것, 그리고 애플리케이션이 우리의 생활을 더 편리하게 해줄 수 있는 것, 내가 지금까지 살면서 한번도 궁금해하지 않고 알지 못한 세계이다. 좋은 개발자가 되서 사회에 도움이 되는 프로그램을 만들고 싶다.
3. 새로 알게 된 내용 :
- 아래 코드는 웹소켓 연결과 관련된 코드이다.
// 기능 : WebRTC를 위한 시그널링 서버 부분으로 요청타입에 따라 분기 처리
@Slf4j
@Component
public class SignalHandler extends TextWebSocketHandler {
@Autowired
private GameRoomService gameRoomService;
@Autowired
private RepositoryService repositoryService;
private final SessionRepository sessionRepository = SessionRepository.getInstance(); // 세션 데이터 저장소
private final ObjectMapper objectMapper = new ObjectMapper();
private static final String MSG_TYPE_JOIN_ROOM = "join_room";
private static final String MSG_TYPE_OFFER = "offer";
private static final String MSG_TYPE_ANSWER = "answer";
private static final String MSG_TYPE_CANDIDATE = "candidate";
@Override
public void afterConnectionEstablished(final WebSocketSession session) {
// 웹소켓이 연결되면 실행되는 메소드
}
// 시그널링 처리 메소드
@Override
protected void handleTextMessage(final WebSocketSession session, final TextMessage textMessage) {
try {
WebSocketMessage message = objectMapper.readValue(textMessage.getPayload(), WebSocketMessage.class);
String userName = message.getSender();
Long roomId = message.getRoomId();
switch (message.getType()) {
// 처음 입장
case MSG_TYPE_JOIN_ROOM:
if (sessionRepository.hasRoom(roomId)) {
// 해당 챗룸이 존재하면
// 세션 저장 1) : 게임방 안의 session List에 새로운 Client session정보를 저장
sessionRepository.addClient(roomId, session);
} else {
// 해당 챗룸이 존재하지 않으면
// 세션 저장 1) : 새로운 게임방 정보와 새로운 Client session정보를 저장
sessionRepository.addClientInNewRoom(roomId, session);
}
// 세션 저장 2) : 이 세션이 어느 방에 들어가 있는지 저장
sessionRepository.saveRoomIdToSession(session, roomId);
// 세션 저장 3) : 방 안에 닉네임들 저장
sessionRepository.addNicknameInRoom(session.getId(), message.getNickname());
Map<String, WebSocketSession> joinClientList = sessionRepository.getClientList(roomId);
// 방안 참가자 중 자신을 제외한 나머지 사람들의 Session ID를 List로 저장
List<String> exportSessionList = new ArrayList<>();
for (Map.Entry<String, WebSocketSession> entry : joinClientList.entrySet()) {
if (entry.getValue() != session) {
exportSessionList.add(entry.getKey());
}
}
Map<String, String> exportNicknameList = new HashMap<>();
for (Map.Entry<String, WebSocketSession> entry : joinClientList.entrySet()) {
if (entry.getValue() != session) {
exportNicknameList.put(entry.getKey(), sessionRepository.getNicknameInRoom(entry.getKey()));
}
}
// 접속한 본인에게 방안 참가자들 정보를 전송
sendMessage(session,
new WebSocketResponseMessage().builder()
.type("all_users")
.sender(userName)
.data(message.getData())
.allUsers(exportSessionList)
.allUsersNickNames(exportNicknameList)
.candidate(message.getCandidate())
.sdp(message.getSdp())
.build());
break;
case MSG_TYPE_OFFER:
case MSG_TYPE_ANSWER:
case MSG_TYPE_CANDIDATE:
if (sessionRepository.hasRoom(roomId)) {
Map<String, WebSocketSession> oacClientList = sessionRepository.getClientList(roomId);
if (oacClientList.containsKey(message.getReceiver())) {
WebSocketSession ws = oacClientList.get(message.getReceiver());
if(!ws.isOpen()){
}
sendMessage(ws,
new WebSocketResponseMessage().builder()
.type(message.getType())
.sender(session.getId()) // 보낸사람 session Id
.senderNickName(message.getNickname())
.receiver(message.getReceiver()) // 받을사람 session Id
.data(message.getData())
.offer(message.getOffer())
.answer(message.getAnswer())
.candidate(message.getCandidate())
.sdp(message.getSdp())
.build());
}
} else {
throw new CustomException(CHAT_ROOM_NOT_FOUND);
}
break;
default:
}
} catch (JsonProcessingException e) {
} catch (Exception e) {
}
}
// 웹소켓 연결이 끊어지면 실행되는 메소드
@Override
public void afterConnectionClosed(final WebSocketSession session, final CloseStatus status) {
String nickname = sessionRepository.getNicknameInRoom(session.getId());
// 끊어진 세션이 어느방에 있었는지 조회
Long roomId = sessionRepository.getRoomId(session);
// 1) 방 참가자들 세션 정보들 사이에서 삭제
sessionRepository.deleteClient(roomId, session);
// 2) 별도 해당 참가자 세션 정보도 삭제
sessionRepository.deleteRoomIdToSession(session);
// 3) 별도 해당 닉네임 리스트에서도 삭제
sessionRepository.deleteNicknameInRoom(session.getId());
// 본인 제외 모두에게 전달
for(Map.Entry<String, WebSocketSession> oneClient : sessionRepository.getClientList(roomId).entrySet()){
sendMessage(oneClient.getValue(),
new WebSocketResponseMessage().builder()
.type("leave")
.sender(session.getId())
.receiver(oneClient.getKey())
.build());
}
Optional<Member> member = repositoryService.findMemberByNickname(nickname);
List<GameRoomAttendee> gameRoomAttendeeList = repositoryService.findAttendeeByRoomId(roomId);
for(GameRoomAttendee gameRoomAttendee : gameRoomAttendeeList) {
if(nickname.equals(gameRoomAttendee.getMemberNickname())){
// 브라우저를 강제로 닫을 경우 종료시키는 웹소켓 연결 끊기
gameRoomService.roomExit(roomId, member.get());
}
}
}
// 메세지 발송
private void sendMessage(WebSocketSession session, WebSocketResponseMessage message) {
try {
String json = objectMapper.writeValueAsString(message);
synchronized (session){
session.sendMessage(new TextMessage(json));
}
}
catch (IOException e) {
}
}
}
- 내가 이해한 바로는, 백엔드에서는 시그널링 서버 역할을 하는 코드를 구현해준다. 웹소켓연결을 할 때, 제일 처음엔 HTTP통신으로 한번 클라이언트끼리 연결을 해줘야 하는데 이때 사용된다. 한번 연결된 클라이언트는 이 이후엔 서버없이 서로 연결이 되어 통신을 한다.
- 프론트엔드 팀원분의 요청으로 MSG_TYPE_JOIN_ROOM, MSG_TYPE_OFFER, MSG_TYPE_ANSWER, MSG_TYPE_CANDIDATE 라는 타입을 만들어서 차례로 프론트에서 이러한 타입들로 들어오는 요청을 그대로 반환해 주는 코드이다.
- 백엔드 코드만 봐선 사실 이해가 잘 되지 않는다. 아래 팀원분의 블로그를 참고해서 흐름을 좀 더 쉽게 이해했다 !
- https://littlezero48.tistory.com/260
4. 셀프칭찬 (오늘 잘한 일) :
- 다른 팀원들의 코드를 이해하기위해 노력했다. 이렇게 시간이 남을 때 풀어지지 않기 위해 계속 우테코 영상도 찾아보고 개념공부를 하려고 노력하고 있다. 존버하는 코린이
5. 내일 할 일 : 서비스 배포 전 마케팅 회의 / 트러블슈팅 정리 / CD 꼭 성공하기 !
[오늘 공부한 부분]
- 코드리뷰
'TIL (Today I Learned)' 카테고리의 다른 글
[75] TIL redis 유효시간 설정, S3오류 수정 (0) | 2023.01.27 |
---|---|
[74] TIL Finally CI/CD !! (0) | 2023.01.26 |
[72] TIL 항해 78일차. 그리고 오늘은 설날 (0) | 2023.01.24 |
[71] WIL 실전프로젝트 3주차 회고 (0) | 2023.01.22 |
[70] TIL 중간회고 발표 (0) | 2023.01.21 |