<aside>
💡
스타트업에서 근무 중, AI 엔지니어였는데 별안간(?) 백엔드 총괄을 맡았습니다.
Python밖에 안해봄/Java는 댄스스포츠밖에 몰랐음/네트워크 배경지식만 쪼금 있었음
한달간의 부트캠프 및 GPT o1 pro와 함께 어찌저찌 해온 과정을 정리해보았습니다.
</aside>
🛠️ 시도한 방법
- EC2 환경에서 스프링부트 및 DB 배포
- EC2(Ubuntu)에 Java 17(OpenJDK) 및 Gradle 설치
- 백엔드 소스코드(스프링부트)를 git clone 또는 SCP로 업로드
./gradlew build 후 java -jar build/libs/...로 애플리케이션 실행
- Security Group 규칙을 열어 8080(스프링), 3306(MySQL) 접근 가능하도록 설정
- MySQL은 EC2 내부에 설치. bind-address=0.0.0.0 설정 및 DB/유저 생성
- WebSocket (STOMP) 기반 채팅
- WebSocketConfig:
@EnableWebSocketMessageBroker + registry.addEndpoint("/ws/chat") → SockJS, STOMP 브로커
ChatController에서 @MessageMapping("/chat") → @SendTo("/topic/public") 구조
- 비로그인 사용자도 익명(principal=null)로 연결 가능
- 로그인 사용자는 쿠키에
jwtToken, JwtChannelInterceptor로 principal 식별하여 DB에 메시지 저장
- SecurityConfig + JWTAuthFilter
- HTTP 레벨에서
JWTAuthFilter(쿠키 기반)로 토큰 검증 → SecurityContextHolder 세팅
SecurityConfig에서 .requestMatchers("/**").permitAll() 설정 → 정적 파일이나 /ws/chat 등 누구나 접근 허용
- WebSocket은 ChannelInterceptor(
JwtChannelInterceptor)에서 토큰을 읽어 optional 인증
- 로컬 GPU 클라이언트 연동
- 클라이언트(파이썬 websockets) → 서버에 Raw WebSocket 시도 시 HTTP 400 발생
- 원인: 서버 측은 STOMP 프로토콜을 사용, Python쪽도 STOMP로 접속해야 handshake가 성립
- 수정안: Python에서 “
Sec-WebSocket-Protocol: v10.stomp”를 포함하여 “CONNECT” 프레임 전송 or STOMP 전용 라이브러리 활용
🚧 어려움/문제점
- 로컬 GPU(Python websockets) ↔ Spring STOMP 간 서버 거절(HTTP 400)
- 스프링부트의
@EnableWebSocketMessageBroker는 STOMP를 전제로 함
- Python websockets 기본은 Raw WS → subprotocol mismatch
- 해결: 클라이언트 측 STOMP 프레임 전송 필요
- JWT / 쿠키 기반 인증 혼동
- HTTP 필터(
JWTAuthFilter)와 WebSocket 핸드셰이크는 별도임
- JwtChannelInterceptor 필요 시, 쿠키에서 “jwtToken=…”를 파싱
- “비로그인도 연결 허용” 설정 여부 명확화 필요했음
- 사무실 Windows GPU 원격 접근(SSH/Tailscale)
- 실제 백엔드와 직접적 관련은 아니지만, 중간에 협업 이슈로 등장
- Tailscale은 상호간 IP를 몰라도 원격접속을 가능하게 하는 훌륭한 서비스지만, windows → 맥으로의 접속은 안 됩니다…………. ㅠㅠ
🔍 배운 점
- Spring STOMP vs Raw WebSocket의 프로토콜 호환성 중요성
- SecurityConfig에서 “requestMatchers”와
ChannelInterceptor의 역할 구분
- EC2 환경에서 MySQL 외부 오픈 시 “bind-address” 설정과 보안 그룹이 필수
- 쿠키 기반 JWT에서 WebSocket 접근 시, 필터와 인터셉터(HTTP vs STOMP) 로직이 별개
- Python 쪽에서 STOMP 사용하려면 별도 라이브러리 or 직접 프레임 구현 필요
🔗 외부 참고 자료 (Optional)