해커톤 기간에는 빠른 기능 구현을 위해 익숙한 코드나 레퍼런스를 참고하여 기능을 완성하는 데 집중했습니다.
프로젝트가 끝난 후, 내가 작성한 코드보다 더 나은 방법은 없는지, 이 기술을 이렇게 사용하는 것이 맞는지라는 의문을 해결하기 위해 이 글을 작성하였습니다.
게임방(Room) 단위로 이벤트를 전송해야 했기에, 서칭을 통해 STOMP 설정을 가져와 적용했습니다. 당시에는 enableSimpleBroker가 정확히 어떤 원리로 작동하는지 모른 채 주소만 맞추면 메시지가 간다는 사실 정도만 알고 코드를 작성하였습니다.
해커톤이 끝난후 새로운 기술에 대한 이해를 위해 CS 이론과 함께 동작 원리를 고민해보았습니다.
registry.addEndpoint("/ws"))가 정확히 언제 개입하는지 분석을 진행했습니다.
[Step 1] L4 Transport Layer: TCP 3-way Handshake
[Step 2] L7 Application Layer: HTTP Upgrade Handshake
GET /ws HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: UpgraderegisterStompEndpoints가 작동합니다./ws임을 확인하고, 해당 엔드포인트가 WebSocketConfig에 등록되어 있는지 검사합니다.HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgraderegistry.addEndpoint("/ws")는 단순한 URL 매핑이 아니라, "HTTP 프로토콜을 WebSocket 프로토콜로 업그레이드해 달라"는 요청을 승인하는 Gateway 역할을 합니다.STOMP과 Raw WebSocket 비교 분석했습니다.
가장 큰 차이는 '메시지를 누구에게 보낼 것인가(Routing)'에 대한 관리 주체입니다.
Raw WebSocket
WebSocketSession 객체는 연결된 상태만 알 뿐, 이 유저가 1번 방에 있는지 2번 방에 있는지 알지 못합니다.Room 1)에 있는 사람들에게만 메시지를 보내려면, 개발자가 직접 메모리에 지도를 그려야 합니다.STOMP
/topic/room/1이라는 주소를 구독하면, 브로커가 내부적으로 "이 세션은 1번 방 메시지를 받겠다"라고 메모해둡니다./topic/room/1로 메시지를 던지면, 브로커가 알아서 해당 주소를 구독한 세션들을 찾아 뿌려줍니다.이 부분은 팀원이 진행하였습니다.
기존에 UserDetails를 구현한 CustomUserPrincipal을 컨트롤러에서 직접 받아서 사용해본 경험이 있기때문에 어떤 방법이 더 나은 방법인지 알기 위해 학습을 진행했습니다.
| 비교 항목 | 기존 방식 (CustomUserPrincipal 직접 사용) | 개선 방식 (@AuthUser 커스텀 어노테이션) |
|---|---|---|
| 장점 | • 별도의 설정 없이 Spring Security가 제공하는 기능을 즉시 사용 가능. • 구현이 빠르고 직관적임. | • 결합도 감소: 컨트롤러가 Security 내부 구현체(CustomUserPrincipal)를 몰라도 됨.• 가독성: @AuthUser만 붙이면 되므로 코드가 간결해짐.• 유연성: Member 객체나 ID 등 원하는 형태로 가공해서 주입 가능. |
| 단점 | • 강한 결합: 컨트롤러 코드가 Spring Security 기술에 종속됨. • 매번 형변환이나 getter 호출이 필요해 코드가 지저분해짐. | • 초기 설정(Annotation 생성, Resolver 등록)에 공수가 들어감. • 내부 동작 원리(Resolver)를 모르면 디버깅이 어려울 수 있음. |
@AuthUser는 컨트롤러 계층과 보안 계층(Spring Security)을 분리해 주는 역할이 부분 또한 팀원이 JWT 토큰 검증 로직을 Interceptor을 통해 구현했습니다.
기존에 저는 filter를 통한 검증 방법을 알고 있었고 filter와 Interceptor로 구현하는 방법의 차이에 호기심을 가지고 Spring의 Request Lifecycle을 분석해 보았습니다.
실행 시점의 차이:
보안 원칙: 잘못된 토큰이나 악의적인 요청은 비즈니스 로직(Spring MVC)이 시작되기 전에 가장 앞단에서 차단해야 리소스 낭비를 막을 수 있음
Trade-off Filter에서 발생하는 예외는 @ControllerAdvice가 잡지 못하는 단점이 있었지만, 이는 Spring Security의 표준인 AuthenticationEntryPoint를 구현하여 해결할 수 있음
설정이 여기저기 흩어져 있는데, 하나로 통일해야 하지 않을까?라는 생각에 학습을 시작했습니다.
Allowed Origins 같은 데이터는 환경(Local/Prod)마다 달라야 하므로 빌드 없이 변경 가능해야 함.Allowed Methods 같은 정책은 변하지 않는 로직이므로 코드에 명시해야 함.해커톤이 끝난 후, 기존에 알던 것과 다른 코드를 분석하여 비교하는 경험을 통해 성장하였습니다.
References