Skip to content

Conversation

@whqtker
Copy link
Member

@whqtker whqtker commented Aug 25, 2025

관련 이슈

작업 내용

  • 핸드셰이크 요청을 보내면 서버에서 다음 로그가 출력됩니다.
    2025-08-25T09:49:40.603Z ERROR 1 --- [nio-8080-exec-8] c.e.s.c.config.CustomHandshakeHandler : "Handshake failed due to invalid Upgrade header: null"
  • 로컬 테스트 결과, 쿼리 파라미터에 토큰을 포함한 초기 웹소켓 핸드셰이크가 정상적으로 작동합니다. 동일한 설정을 유지하고, localhost:8080 을 개발 서버 주소로 바꾸면 연결이 되지 않습니다. 코드보다 다른 설정에 문제가 있을 가능성이 높다고 생각했고, 이에 nginx.conf 를 수정합니다. [참고], [참고]
  • 현재는 nginx.conf 를 수정하여도 develop 브랜치에 머지 시 실제 개발 서버에 반영되지 않습니다. 이에 dev-cd.yml 을 수정합니다.
  • 테스트 코드를 더 견고하게 수정합니다.
  • 더 자세한 내용은 코멘트 달았습니다.

### nginx.conf 를 버전 관리하는 방향으로 결정되었으므로 dev, prod 별로 설정 파일 분리한 후 추가 반영하겠습니다.

특이 사항

웹소켓 핸드셰이크 시 HTTP 버전은 1.1 이상이어야 합니다. [참고]

리뷰 요구사항 (선택)

  • Nginx, CI/CD 프로세스에 있어 잘 아는 건 아니라서, 놓치고 있는 부분이 있을 수 있습니다 ..! 있다면 말씀해주세요 !!

@whqtker whqtker self-assigned this Aug 25, 2025
@whqtker whqtker added the 버그 Something isn't working label Aug 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 25, 2025

Walkthrough

  1. CI 배포 워크플로우 변경: scp로 nginx 설정을 원격의 전용 디렉터리에 업로드하고, SSH로 해당 파일을 /etc/nginx/conf.d/default.conf로 복사·검증(nginx -t)·리로드한 뒤 docker compose -f [env].yml downdocker compose -f [env].yml up -d --build를 실행합니다.
  2. DEV nginx 수정: api.stage.solid-connection.comserver_name으로 추가하고 TLS 경로를 stage용으로 변경했으며 프록시 대상은 localhost:8080으로 바꾸고 WebSocket 업그레이드 헤더를 추가했습니다.
  3. PROD nginx 신규 추가: HTTP에서 HTTPS로 301 리다이렉트하고 HTTPS 블록에 상세한 TLS 설정, 표준 프록시 헤더 및 업그레이드 처리를 포함하는 docs/infra-config/nginx.prod.conf를 추가했습니다.
  4. 테스트 업데이트: 테스트 클래스명이 WebSocketHandshakeTest로 변경되고 DisplayName과 핸드셰이크 URL 스킴이 ws://에서 http://로 변경되었습니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wibaek
  • nayonsoso
  • lsy1307
  • Gyuhyeok99

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed 제목 "fix: Nginx가 WebSocket Handshake 요청을 올바르게 처리하도록"은 PR의 핵심 목적(nginx 설정 수정으로 WebSocket 핸드셰이크 문제 해결)을 간결하고 명확하게 요약하며, 변경된 파일군(nginx 설정, CI/CD 파이프라인, 테스트 보강)과 일치합니다. 동료가 히스토리를 스캔할 때 주요 의도를 바로 파악할 수 있어 적절합니다.
Linked Issues Check ✅ Passed 링크된 이슈 [#480] 관점에서 주요 목표 달성 여부는 다음과 같이 요약됩니다: 1. clnginx 설정 변경 — docs/infra-config/nginx.dev.conf에 proxy_http_version 1.1 및 proxy_set_header Upgrade/Connection 등이 추가되어 WebSocket 핸드셰이크 처리 요건을 충족합니다; 2. clCI/CD(dev) 반영 — .github/workflows/dev-cd.yml이 scp/ssh로 nginx 파일을 원격에 복사·검증·재적용하도록 변경되어 병합 시 설정이 실제 서버에 반영됩니다; 3. cl테스트 보강 — 테스트 클래스와 설정이 핸드셰이크 검증 의도를 반영하도록 수정되어 회귀 방지에 기여합니다. 종합하면 코드 변경은 링크된 이슈의 핵심 구현 목표와 정합합니다.
Out of Scope Changes Check ✅ Passed 검토 결과 대부분의 변경은 이슈 #480의 범위(nginx 설정 수정, dev에 대한 CI/CD 반영, 테스트 보강)와 직접적으로 연관되어 있습니다. PR에 prod용 nginx 설정 추가 및 .github/workflows/prod-cd.yml 변경이 포함되어 범위가 다소 확장되었으나 PR 설명에서 dev/prod 분리 의도를 언급하고 있어 무관한 변경으로 보기 어렵습니다. 따라서 눈에 띄는 '무관한' 변경은 발견되지 않았습니다.
Description Check ✅ Passed PR 설명은 '관련 이슈'에 resolves: #480을 포함하고 문제 현상, 원인 추정, 수행 변경(nginx 설정 수정, dev-cd.yml 변경, 테스트 보강)을 명확히 기술해 템플릿의 핵심 항목을 충족합니다. 또한 '특이 사항'에 WebSocket 핸드셰이크의 HTTP/1.1 요구사항을 명시해 검토에 필요한 배경 정보를 제공합니다. 다만 파일별 변경 요약과 배포 후 검증(예: 개발 서버에서 핸드셰이크 성공 확인)을 한두 문장 추가하면 더 완전합니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
.github/workflows/dev-cd.yml (1)

84-84: 단계 이름을 더 명확하게 수정하는 것을 제안합니다.

현재 단계 이름이 두 가지 작업(docker compose 실행과 nginx 설정 적용)을 모두 포함하고 있어 다소 모호합니다.

-      - name: Run docker compose and apply nginx config
+      - name: Apply nginx config and run docker compose
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7060378 and 9a1343d.

📒 Files selected for processing (2)
  • .github/workflows/dev-cd.yml (1 hunks)
  • docs/infra-config/nginx.conf (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
docs/infra-config/nginx.conf (1)

39-41: WebSocket 지원을 위한 필수 헤더 추가가 올바릅니다!

WebSocket 핸드셰이크와 연결 업그레이드를 위한 세 가지 핵심 설정이 정확히 적용되었습니다:

  1. proxy_http_version 1.1: HTTP/1.1 프로토콜 사용으로 업그레이드 지원
  2. proxy_set_header Upgrade $http_upgrade: 클라이언트의 Upgrade 헤더를 백엔드로 전달
  3. proxy_set_header Connection "upgrade": 연결 업그레이드 요청을 백엔드로 전달

이 설정으로 쿼리 파라미터에 포함된 토큰과 함께 WebSocket 핸드셰이크가 정상적으로 처리될 것입니다.

.github/workflows/dev-cd.yml (3)

74-83: nginx 설정 파일 복사 단계가 적절히 추가되었습니다.

새로운 단계에서 nginx.conf를 원격 서버의 전용 nginx 디렉토리로 복사하고 default로 이름을 변경하는 것이 명확합니다. 파일 경로와 대상 디렉토리 구조가 논리적으로 구성되어 있습니다.


92-95: nginx 설정 적용 순서와 검증 로직이 우수합니다.

설정 적용 과정이 체계적으로 구성되었습니다:

  1. 설정 파일을 올바른 위치로 복사
  2. nginx -t로 설정 검증
  3. nginx -s reload로 안전한 재시작
  4. script_stop: true로 실패 시 워크플로 중단

이는 nginx 설정 배포의 모범 사례를 잘 따르고 있습니다.


96-98: 검증 완료: docker compose 실행 순서 문제 없음

  1. 검증 내용 확인
    • docs/infra-config/nginx.conf에서 solid-connection-server:8080 upstream 참조를 확인했습니다.
    • docker-compose.dev.yml에서 solid-connection-server 서비스의 ports: "8080:8080" 매핑을 확인했습니다.
  2. 검증 결과
    • nginx 재시작 후 docker compose down/up 순서로 실행해도 타이밍 이슈는 발견되지 않았습니다.
    • Redis 의존성(depends_on: redis)도 올바르게 설정되어 있어 서비스 간 순서 충돌이 없습니다.
  3. 선택 권장 사항
    • 컨테이너 준비 상태 보장을 위해 Docker healthcheck 또는 wait-for-it 스크립트 도입을 고려해보세요.
  4. 결론
    • 현 워크플로우 순서는 논리적으로 타당하므로 리뷰 코멘트는 해제합니다.

@whqtker whqtker marked this pull request as draft August 25, 2025 01:28
@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from 9a1343d to 873025e Compare August 25, 2025 03:42
@whqtker whqtker marked this pull request as ready for review August 25, 2025 03:49
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
docs/infra-config/nginx.conf (1)

39-69: 스테이징 서버 설정에서 SSL 구성 방식의 불일치를 확인해주세요.

스테이징 서버는 /etc/letsencrypt/options-ssl-nginx.conf를 include하고 ssl_dhparam을 사용하는 반면, 프로덕션 서버는 SSL 설정을 직접 명시하고 있습니다. 두 환경 간 일관성을 위해 동일한 방식을 사용하는 것이 좋습니다.

프로덕션 서버도 Let's Encrypt의 표준 설정을 사용하도록 수정:

 server {
     listen 443 ssl;
     server_name api.solid-connection.com;
 
     ssl_certificate /etc/letsencrypt/live/api.solid-connection.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/api.solid-connection.com/privkey.pem;
+    include /etc/letsencrypt/options-ssl-nginx.conf;
+    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
     client_max_body_size 10M;
 
-    ssl_protocols TLSv1.2 TLSv1.3;
-    ssl_prefer_server_ciphers on;
-    ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
-    ssl_session_cache shared:SSL:10m;
-    ssl_session_timeout 10m;
-    ssl_stapling on;
-    ssl_stapling_verify on;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9a1343d and 873025e.

📒 Files selected for processing (3)
  • .github/workflows/dev-cd.yml (1 hunks)
  • docs/infra-config/nginx.conf (2 hunks)
  • src/main/java/com/example/solidconnection/security/config/SecurityConfiguration.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/dev-cd.yml
🔇 Additional comments (4)
src/main/java/com/example/solidconnection/security/config/SecurityConfiguration.java (1)

65-65: WebSocket 핸드셰이크를 위한 적절한 보안 설정입니다!

/connect/** 경로를 permitAll()로 변경한 것은 WebSocket 핸드셰이크 과정에서 쿼리 파라미터로 전달되는 토큰 기반 인증을 원활하게 처리하기 위한 올바른 접근입니다. WebSocket의 초기 연결은 HTTP 핸드셰이크로 시작되므로 이 단계에서 Spring Security의 기본 인증을 우회하는 것이 필요합니다.

docs/infra-config/nginx.conf (3)

33-36: WebSocket 지원을 위한 핵심 헤더가 올바르게 추가되었습니다!

HTTP/1.1 프로토콜 버전 설정과 Upgrade/Connection 헤더 설정이 WebSocket 핸드셰이크에 필수적인 요소들입니다. 이 설정으로 Nginx가 WebSocket 연결을 올바르게 프록시할 수 있게 됩니다.


65-67: 스테이징 서버의 WebSocket 설정이 적절하게 구성되었습니다!

프로덕션과 동일한 WebSocket 지원 헤더가 올바르게 추가되어 일관된 동작을 보장합니다.


71-83: 🔒 보안 점검 요청: nginx 기본 서버 블록과 CI/CD 파이프라인 관리

  1. CI/CD 파이프라인에서 /var/www/html 처리 확인

    • rg -n "/var/www/html" --type yaml -g "*.yml" 실행 결과, .yml 파일 내에 해당 경로가 발견되지 않았습니다.
    • 배포 스크립트나 파이프라인 설정에서 이 디렉터리를 어떻게 초기화·배포하는지 수동으로 검토해주세요.
  2. /var/www/html 디렉터리 존재 여부 및 콘텐츠 검증

    • 실제 서버에 /var/www/html 디렉터리가 존재하는지 확인이 필요합니다.
    • 의도하지 않은 정보 노출을 방지하기 위해 빈 디렉터리이거나 최소한의 정적 페이지만 제공되는지 검증 부탁드립니다.
  3. 디렉터리 권한 및 접근 제어 점검

    • 디렉터리 권한과 소유권이 적절히 설정되어 있는지 확인해주세요.
    • 민감한 파일이 포함되어 있지 않도록 추가 점검을 권장합니다.

@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from 873025e to 3d17d85 Compare August 25, 2025 04:04
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (5)

69-72: 쿼리 파라미터 토큰은 안전하게 인코딩합시다.

  1. 현재 문자열 연결은 JWT 등 특수문자 포함 시 엣지 케이스가 생길 수 있습니다.
  2. UriComponentsBuilder로 인코딩/조립을 위임하면 안전성과 가독성이 좋아집니다.

아래와 같이 변경을 제안합니다.

-            String tokenUrl = url + "?token=" + accessToken.token();
+            String tokenUrl = UriComponentsBuilder.fromHttpUrl(url)
+                    .queryParam("token", accessToken.token())
+                    .build(true)
+                    .toUriString();

추가 import가 필요합니다(선택적 지원 코드):

import org.springframework.web.util.UriComponentsBuilder;

81-89: 보안 설정(permitAll)과 테스트 기대치(401) 사이에 불일치 가능성이 있어요.

  1. PR 요약에 따르면 핸드셰이크 엔드포인트를 permitAll로 열 계획입니다. 이 경우 미인증 사용자의 “핸드셰이크” 자체는 성공해야 하고, 인증 실패는 STOMP CONNECT/구독 단계에서 발생하는 게 자연스럽습니다.
  2. 현재 테스트는 미인증 사용자가 핸드셰이크 단계에서 401을 기대하고 있어, 보안 설정이 변경되면 테스트가 깨질 수 있습니다.

보안 설정 방향을 확정해 주세요. permitAll이라면 아래 둘 중 하나로 테스트를 조정하는 것을 권합니다.
1) “미인증도 핸드셰이크는 성공”으로 기대치를 바꾸고, 보호 대상 구독/전송 시점에 접근 거부를 검증.
2) STOMP CONNECT 프레임 처리에서 인증 실패를 받도록 커스텀 StompSessionHandler로 ERROR/transport error를 포착.

참고로, 현 테스트는 예외 cause를 HttpClientErrorException.Unauthorized로 고정하고 있어 환경/클라이언트 구현(예: DeploymentException, WebSocketHandshakeException) 변화에 취약합니다. 필요 시 메시지/상태코드 기반으로 좀 더 관대한 매칭을 고려해 주세요.

원하시면 permitAll 시나리오에 맞춘 대안 테스트 템플릿을 드릴게요.


84-87: 타임아웃 표기 일관성 정리(가독성).

  1. 위에서는 SECONDS를 static import로 사용하고, 여기서는 TimeUnit.SECONDS를 사용 중입니다.
  2. 하나로 통일하면 눈에 더 잘 들어옵니다.
-                }).get(5, TimeUnit.SECONDS);
+                }).get(5, SECONDS);

(위 변경 시 import java.util.concurrent.TimeUnit;가 불필요해질 수 있으니 IDE가 정리하도록 두면 됩니다.)


70-72: 보안 관점 안내: 쿼리스트링 토큰은 로그/프록시 레이어에 남을 수 있어요.

  1. Nginx 접근로그/레퍼러/에러로그에 토큰이 노출될 소지가 있어 운영 보안 리스크가 있습니다.
  2. 가능하다면 WebSocket 핸드셰이크에서 Sec-WebSocket-Protocol 헤더를 통한 Bearer 전달, 혹은 STOMP CONNECT 헤더로의 이전을 검토해 주세요.

인프라 제약으로 쿼리스트링을 유지해야 한다면, Nginx에서 $args 마스킹, 접근로그 필터링, 리라이트 단계에서 토큰을 헤더로 옮기고 쿼리 제거 등 보완책을 같이 설계할 수 있습니다.


48-54: 변경 사항 검증 완료 및 주석 추가 제안

  1. SockJS 활성화 여부 검증
     확인했습니다. StompWebSocketConfig.registerStompEndpoints 메서드에서 .withSockJS()가 호출되어 서버 측 엔드포인트에 SockJS가 정상적으로 활성화되어 있습니다.

  2. 의도 명시 주석 추가 제안
     권장합니다. WebSocketHandshakeTest.setUp의 URL 할당부 바로 위에 의도를 설명하는 한 줄 주석을 추가하여 후속 변경 시 혼선을 방지합시다.
      diff   - this.url = String.format("http://localhost:%d/connect", port);   + // SockJS base URL은 http(s)이며, WebSocketTransport가 내부적으로 ws(s)로 업그레이드합니다.   + this.url = String.format("http://localhost:%d/connect", port);   

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 873025e and 3d17d85.

📒 Files selected for processing (1)
  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T18:07:08.348Z
Learnt from: nayonsoso
PR: solid-connection/solid-connect-server#400
File: src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java:20-22
Timestamp: 2025-07-23T18:07:08.348Z
Learning: SockJS configuration should be added to STOMP endpoints using .withSockJS() for better browser compatibility and network environment support, even if frontend doesn't initially use SockJS client libraries.

Applied to files:

  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (1)

33-34: 클래스/표시명 정리: 테스트 목적이 더 명확해졌어요.

  1. “WebSocket Handshake 테스트”로 명확한 스코프가 드러납니다.
  2. 파일/클래스명 변경에 따른 IDE 리팩터가 잘 반영되었다면 문제없습니다.

@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from 3d17d85 to 3221464 Compare August 25, 2025 04:25
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (2)

56-61: 세션 종료와 함께 클라이언트도 정리해 리소스 누수를 예방합시다.

  1. 현재는 세션만 끊습니다.
  2. WebSocketStompClient의 스케줄러/실행자 자원이 남을 수 있으므로 tearDown에서 stompClient.stop() 호출을 권장합니다.

아래처럼 정리 코드를 추가해 주세요.

 @AfterEach
 void tearDown() {
   if (this.stompSession != null && this.stompSession.isConnected()) {
     this.stompSession.disconnect();
   }
+  if (this.stompClient != null) {
+    this.stompClient.stop();
+  }
 }

84-86: 시간 단위 표기를 통일하면 가독성이 좋아집니다.

  1. 상단에 SECONDS를 static import 하셨으니 여기서도 동일하게 쓰는 편이 일관됩니다.
-            }).get(5, TimeUnit.SECONDS);
+            }).get(5, SECONDS);
docs/infra-config/nginx.conf (3)

27-36: WebSocket 프록시의 안정성을 높이는 타임아웃·버퍼 설정을 추가해 주세요.

  1. 장시간 유휴 상태의 WS 연결에서 기본 타임아웃으로 끊기는 이슈를 선제 차단합니다.
  2. 업그레이드 요청 시 버퍼링을 끄고 캐시 우회를 명시해 디버깅과 지연을 줄입니다.
     location / {
         proxy_pass http://solid-connection-server:8080;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
+        proxy_read_timeout 600s;
+        proxy_send_timeout 600s;
+        proxy_buffering off;
+        proxy_cache_bypass $http_upgrade;
     }

12-13: 프로덕션 TLS 설정을 스테이징과 동일 수준으로 정렬합시다.

  1. IPv6 리스닝을 추가해 이중 스택 환경에서의 접근성을 확보합니다.
  2. Let’s Encrypt 권장 옵션(options-ssl-nginx.conf, ssl_dhparams)을 포함해 보수성을 높입니다.
 server {
-    listen 443 ssl;
+    listen 443 ssl;
+    listen [::]:443 ssl;
     server_name api.solid-connection.com;

     ssl_certificate /etc/letsencrypt/live/api.solid-connection.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/api.solid-connection.com/privkey.pem;
     client_max_body_size 10M;

     ssl_protocols TLSv1.2 TLSv1.3;
     ssl_prefer_server_ciphers on;
     ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
     ssl_session_cache shared:SSL:10m;
     ssl_session_timeout 10m;
     ssl_stapling on;
     ssl_stapling_verify on;
+    include /etc/letsencrypt/options-ssl-nginx.conf;
+    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

Also applies to: 19-26


59-68: 스테이징 WebSocket 프록시에도 동일한 타임아웃·버퍼 설정을 적용해 주세요.

  1. 환경 간 설정 차이를 줄여 재현성과 예측 가능성을 높입니다.
     location / {
         proxy_pass http://solid-connection-dev:8080;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
+        proxy_read_timeout 600s;
+        proxy_send_timeout 600s;
+        proxy_buffering off;
+        proxy_cache_bypass $http_upgrade;
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d17d85 and 3221464.

📒 Files selected for processing (3)
  • .github/workflows/dev-cd.yml (1 hunks)
  • docs/infra-config/nginx.conf (2 hunks)
  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/dev-cd.yml
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T18:07:08.348Z
Learnt from: nayonsoso
PR: solid-connection/solid-connect-server#400
File: src/main/java/com/example/solidconnection/chat/config/StompWebSocketConfig.java:20-22
Timestamp: 2025-07-23T18:07:08.348Z
Learning: SockJS configuration should be added to STOMP endpoints using .withSockJS() for better browser compatibility and network environment support, even if frontend doesn't initially use SockJS client libraries.

Applied to files:

  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (3)

33-35: 이름·표기 정리가 테스트 의도를 또렷하게 만들었습니다.

  1. 클래스명 변경(WebSocketHandshakeTest)과 DisplayName 갱신으로 테스트 범위가 ‘핸드셰이크’임이 즉시 드러납니다.
  2. 팀 합의된 네이밍 규칙과도 잘 어울립니다.

81-89: 보안 설정 확인 결과

테스트가 기대하는 401 Unauthorized 응답은 보안 설정과 일치합니다.
/connect/** 엔드포인트는 인증이 필요한 설정(.authenticated())으로 등록되어 있습니다.
접근제어 설정은 다음과 같습니다:

  1. Security 필터 설정:
    • .requestMatchers("/connect/**").authenticated()
  2. 관리자 전용 경로:
    • .requestMatchers("/admin/**").hasRole(ADMIN.name())
  3. 그 외 전부:
    • .anyRequest().permitAll()

따라서, 핸드셰이크 시 인증 정보 없이 요청할 경우 Unauthorized(401)가 발생하는 현재 테스트를 그대로 유지해도 무방합니다.

Likely an incorrect or invalid review comment.


48-54: ✅ 서버 SockJS 설정 확인 및 테스트 승인

  1. .withSockJS() 등록 확인
    • StompWebSocketConfig.java 파일(36행)에서 .withSockJS() 호출을 발견했습니다.
  2. 클라이언트 테스트 구성 검토
    • WebSocketHandshakeTest의 base URL(http://localhost:{port}/connect)과 SockJsClient 설정이 서버 엔드포인트와 호환됨을 확인했습니다.
  3. 리뷰 승인
    • 추가 수정 없이 이 테스트 구성을 승인합니다.
docs/infra-config/nginx.conf (2)

71-84: 기본 서버 블록 구성은 안전한 디폴트로 보입니다.

  1. 80 default_server로 404 처리하는 구성이 다른 서버 블록들과 충돌을 줄입니다.
  2. 운영 과정에서 정적 헬스체크 페이지가 필요하면 차후 location /healthz 정도를 경량으로 추가하는 방안을 고려해도 좋겠습니다.

60-61: 검증 요청: proxy_pass 구성 확인 워크스루

  1. Compose 파일 위치 확인하기.
    • 프로젝트에서 실제 사용하는 docker-compose.yml 또는 유사 파일 경로를 알려주세요.
  2. 서비스명 DNS 해석 검증하기.
    • nginx가 실행되는 네임스페이스(예: Docker 네트워크) 내에서 solid-connection-dev가 올바르게 DNS로 해석되는지 확인해주세요.
  3. 포트 노출 설정 살펴보기.
    • 해당 서비스가 8080 포트를 외부에 노출하고 있는지 검토 부탁드립니다.
  4. 호스트 OS 프록시 시나리오 점검하기.
    • 호스트 OS의 nginx가 컨테이너를 프록시하는 경우, 127.0.0.1:포워딩포트로의 프록시 설정이 필요한지 확인해주세요.

@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from 3221464 to 82321bd Compare August 25, 2025 10:38
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
docs/infra-config/nginx.conf (2)

35-44: 1) WebSocket 업그레이드 처리: Connection 헤더를 조건부로 바꾸고 타임아웃을 추가해 주세요.

  • 현재 proxy_http_version 1.1Upgrade/Connection 헤더 추가는 방향이 정확합니다.
  • 다만 Connection "upgrade"를 항상 강제하면 일반 HTTP 요청에도 전파되어 예상치 못한 동작을 유발할 수 있어요.
  • map $http_upgrade $connection_upgrade를 선언해 조건부로 전파하고, 긴 WS 세션 유지를 위해 proxy_read_timeout/proxy_send_timeout을 늘리는 것을 권장합니다.

아래 diff를 적용하면 됩니다.

 location / {
     proxy_pass http://localhost:8080;
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header X-Forwarded-Proto $scheme;
-    proxy_http_version 1.1;
-    proxy_set_header Upgrade $http_upgrade;
-    proxy_set_header Connection "upgrade";
+    proxy_http_version 1.1;
+    proxy_set_header Upgrade $http_upgrade;
+    proxy_set_header Connection $connection_upgrade;
+    proxy_read_timeout 600s;
+    proxy_send_timeout 600s;
+    proxy_buffering off;
 }

또한, 동일 파일 상단(서버 블록 위)에 아래 블록을 추가해 주세요.

# WebSocket Connection 헤더 조건부 전파
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

21-34: 3) TLS 설정 보강: OCSP 검증용 신뢰 체인과 TLSv1.3 별도 스위트 고려.

  • ssl_stapling_verify on을 사용하는 경우, 신뢰 체인 파일을 ssl_trusted_certificate로 명시하는 것을 권장합니다.
  • 또한 TLSv1.3은 ssl_ciphers에 영향받지 않으므로 필요 시 ssl_ciphersuites를 별도로 지정할 수 있어요.

예시:

 server {
     listen 443 ssl;
     server_name api.stage.solid-connection.com;

     ssl_certificate /etc/letsencrypt/live/api.stage.solid-connection.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/api.stage.solid-connection.com/privkey.pem;
+    ssl_trusted_certificate /etc/letsencrypt/live/api.stage.solid-connection.com/chain.pem;
     client_max_body_size 10M;

     ssl_protocols TLSv1.2 TLSv1.3;
     ssl_prefer_server_ciphers on; # 클라이언트 보다 서버의 암호화 알고리즘을 우선하도록 설정
     ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
+    # 필요 시 TLSv1.3 스위트 지정(선택)
+    # ssl_ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
     ssl_session_cache shared:SSL:10m; # SSL 세션 캐시 설정
     ssl_session_timeout 10m;
     ssl_stapling on; # OCSP 스테이플링 활성화
     ssl_stapling_verify on;
.github/workflows/dev-cd.yml (2)

74-83: 3) 원격 디렉터리 보장 및 액션 버전 고정으로 안정성을 높여 주세요.

  • target: ".../solid-connection-dev/nginx" 경로가 없으면 업로드가 실패할 수 있습니다.
  • 또한 appleboy/*@master 사용은 서드파티 액션 공급망 리스크가 큽니다. 태그 또는 커밋 SHA로 핀닝해 주세요.

예시 수정안입니다.

   - name: Copy nginx config to remote
-    uses: appleboy/scp-action@master
+    uses: appleboy/scp-action@<고정_버전_또는_SHA>
     with:
       host: ${{ secrets.DEV_HOST }}
       username: ${{ secrets.DEV_USERNAME }}
       key: ${{ secrets.DEV_PRIVATE_KEY }}
       source: "./docs/infra-config/nginx.conf"
       target: "/home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx"
       rename: "default.conf"

추가로, 업로드 전 디렉터리를 보장하는 단계를 한 줄 넣어 주세요.

+  - name: Prepare remote directories
+    uses: appleboy/ssh-action@<고정_버전_또는_SHA>
+    with:
+      host: ${{ secrets.DEV_HOST }}
+      username: ${{ secrets.DEV_USERNAME }}
+      key: ${{ secrets.DEV_PRIVATE_KEY }}
+      script: |
+        mkdir -p /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx

84-91: 4) Nginx 검증 흐름은 좋습니다. 리로드 실패 시 즉시 중단도 적절합니다.

  • script_stop: truenginx -tnginx -s reload 순서가 안전합니다.
  • 작은 보강으로 set -euo pipefail 선행과 sudo nginx -T(선택) 출력 저장을 고려해 보세요.

예시:

-          script: |
-            sudo cp /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default /etc/nginx/conf.d/default.conf
+          script: |
+            set -euo pipefail
+            sudo cp /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default.conf /etc/nginx/conf.d/default.conf
             sudo nginx -t
             sudo nginx -s reload
+            # sudo nginx -T | sed -n '1,80p'  # (선택) 설정 요약 출력
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3221464 and 82321bd.

📒 Files selected for processing (3)
  • .github/workflows/dev-cd.yml (1 hunks)
  • docs/infra-config/nginx.conf (3 hunks)
  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
docs/infra-config/nginx.conf (1)

1-4: 4) 도메인/인증서/리디렉트 합치성 점검: 스테이징 도메인과 클라이언트 스킴 확인.

  • server_name과 인증서 경로가 api.stage.solid-connection.com으로 고정되어 있습니다.

  • 이 파일이 개발 서버에 그대로 배포된다면, 해당 호스트에 인증서가 실제 배치되어 있는지와 DNS가 가리키는지를 반드시 확인해 주세요.

  • 80→443의 301 리디렉트는 HTTP 클라이언트엔 문제없지만, 일부 WebSocket 클라이언트는 ws://에서 https://로의 리디렉트를 따르지 않거나 wss://로 자동 전환하지 않습니다.

  • 모든 클라이언트가 wss://api.stage.solid-connection.com/...로 직접 접속하도록 구성됐는지, 그리고 테스트 코드도 동일 스킴을 사용하도록 정합성이 맞는지 점검해 주세요.

proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebSocket Handshake 관련 설정입니다

key: ${{ secrets.DEV_PRIVATE_KEY }}
script_stop: true
script: |
sudo cp /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default /etc/nginx/conf.d/default.conf
Copy link
Member Author

@whqtker whqtker Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

권한 이슈가 있을 수 있으므로 'Copy nginx config to remote' 단계에서 파일을 임시 경로에 복사한 후 대체합니다.


location / {
proxy_pass http://solid-connection-server:8080;
proxy_pass http://localhost:8080;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker-compose.dev.yml 에서 network_mode: host 가 설정되어 있습니다. Nginx와 도커 컨테이너가 같은 네트워크를 공유하게 되므로, localhost 로 변경합니다.

@@ -1,5 +1,6 @@
server {
listen 80;
server_name api.stage.solid-connection.com;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인증서 관련 문제가 발생했어서 추가했습니다.

ssl_certificate /etc/letsencrypt/live/api.solid-connection.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.solid-connection.com/privkey.pem;
ssl_certificate /etc/letsencrypt/live/api.stage.solid-connection.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.stage.solid-connection.com/privkey.pem;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stage 서버이고, 실제로 stage 이름을 가지는 디렉터리 및 인증서가 존재합니다.

@BeforeEach
void setUp() {
this.url = String.format("ws://localhost:%d/connect", port);
this.url = String.format("http://localhost:%d/connect", port);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

웹소켓 핸드셰이크 시 ws가 아니라 https 프로토콜을 사용합니다. 단, SSL 관련 설정이 없기에, http로 임시로 변경합니다.

@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from 82321bd to 6537bd3 Compare August 25, 2025 11:00
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (9)
docs/infra-config/nginx.dev.conf (3)

41-43: 1) WebSocket Connection 헤더를 map으로 안전하게 처리하세요.
모든 요청에 Connection "upgrade"가 붙으면 비-WebSocket 요청에서 부작용이 날 수 있습니다. WebSocket 요청에서만 upgrade로, 그 외에는 close로 보내도록 분기하는 것이 안전합니다.

  • 변경 제안
      1. location 블록에서는 $connection_upgrade 변수를 사용합니다.
      1. 파일 상단(http 컨텍스트 내)에 map 블록을 선언합니다.

적용 diff(해당 위치 내 교체):

-        proxy_set_header Connection "upgrade";
+        proxy_set_header Connection $connection_upgrade;

파일 최상단(서버 블록들보다 위)에 추가(참고용 코드 블록):

# WebSocket 업그레이드 여부에 따라 Connection 헤더를 안전하게 분기
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

35-44: 2) 장시간 WebSocket 연결을 위한 timeout·버퍼 설정을 추가하세요.
Idle WS가 60s 전후 기본 타임아웃으로 끊길 수 있습니다. 읽기/쓰기 타임아웃 연장과 버퍼링 off를 권장합니다.

  • 변경 제안
      1. 긴 폴링/WS 환경에서 끊김 방지를 위해 타임아웃을 늘립니다.
      1. WS는 스트리밍 특성상 프록시 버퍼링을 끄는 것이 일반적입니다.

적용 diff(해당 location 블록 내부에 추가):

         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection "upgrade";
+        proxy_read_timeout 3600s;
+        proxy_send_timeout 3600s;
+        proxy_buffering off;

36-36: 3) upstream을 localhost → 127.0.0.1로 통일하는 것을 권장합니다.
일부 배포에서 localhost가 IPv6(::1)로 해석되거나 컨테이너 경계와 혼선이 생길 수 있습니다. prod와 동일하게 127.0.0.1로 맞추면 혼동이 줄어듭니다.

적용 diff:

-        proxy_pass http://localhost:8080;
+        proxy_pass http://127.0.0.1:8080;
  • 확인 팁
      1. Nginx가 호스트 OS에서 구동되고, 애플리케이션이 같은 호스트의 8080에 바인딩되어 있는지 확인해주세요.
      1. 만약 둘 다 Docker 네트워크 내라면, 컨테이너 서비스 이름(upstream 블록) 사용이 더 적합할 수 있습니다.
docs/infra-config/nginx.prod.conf (3)

31-33: 1) prod도 Connection 헤더를 map으로 안전하게 분기하세요.
WS 외 요청에 무조건 upgrade를 보내지 않도록 dev와 동일한 패턴을 적용하는 것이 안전합니다.

  • 변경 제안
      1. 아래와 같이 $connection_upgrade 변수로 교체합니다.
      1. 파일 상단(http 컨텍스트)에는 동일한 map 블록을 추가합니다.

적용 diff:

-        proxy_set_header Connection "upgrade";
+        proxy_set_header Connection $connection_upgrade;

파일 최상단에 추가(참고용 코드 블록):

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

25-34: 2) WebSocket 안정성 강화를 위해 timeout·버퍼 설정을 추가하세요.
WS 유휴 연결 유지와 지연 최소화를 위해 타임아웃 연장 및 버퍼링 off 권장합니다.

적용 diff(해당 location 블록 내부에 추가):

         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
-        proxy_set_header Connection "upgrade";
+        proxy_set_header Connection $connection_upgrade;
+        proxy_read_timeout 3600s;
+        proxy_send_timeout 3600s;
+        proxy_buffering off;
  • 선택 사항
      1. 클라이언트가 subprotocol을 쓴다면 proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol; 추가를 고려하세요.

4-6: 3) 301 대신 308로 리다이렉트 고정(method 보존) 고려
POST/특수 메서드 보존 측면에서 308이 더 엄격합니다. WS 핸드셰이크는 GET이라 영향은 적지만, 전반 규칙 일관성 관점에서 고려할 만합니다.

적용 diff:

-            return 301 https://$host$request_uri;
+            return 308 https://$host$request_uri;
.github/workflows/prod-cd.yml (3)

74-83: 1) 원격 디렉터리 존재 보장 및 액션 버전 pin을 권장합니다.
배포 안정성을 위해 대상 디렉터리 생성과 액션 버전 고정이 유용합니다.

  • 변경 제안
      1. 사전 디렉터리 생성 단계 추가(없으면 scp가 실패할 수 있음).
      1. @master 대신 릴리스 태그나 커밋 SHA로 pin(서플라이 체인 리스크 완화).

예시(새 단계 추가 — 참고용 코드 블록):

- name: Ensure nginx target directory exists
  uses: appleboy/ssh-action@v0.1.10 # 또는 커밋 SHA pin
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.PRIVATE_KEY }}
    script: |
      mkdir -p /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx
  • 액션 pin 예시(참고용, 실제 버전은 조직 표준에 맞춰 선택):
uses: appleboy/scp-action@v0.1.7
# uses: appleboy/ssh-action@<commit-sha>

92-99: 2) nginx 설정 적용 시 실패 자동 롤백을 넣어 장애 폭을 줄이세요.
새 설정이 유효하지 않을 때 즉시 이전 설정으로 복구되도록 가드하면 안전합니다.

적용 diff(스크립트 부분 교체):

-          sudo cp /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx/default.conf /etc/nginx/conf.d/default.conf
-          sudo nginx -t
-          sudo nginx -s reload
-          
-          cd /home/${{ secrets.USERNAME }}/solid-connect-server
-          docker compose -f docker-compose.prod.yml down
-          docker compose -f docker-compose.prod.yml up -d --build
+          set -euo pipefail
+
+          # 기존 설정 백업
+          if [ -f /etc/nginx/conf.d/default.conf ]; then
+            sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
+          fi
+
+          # 신규 설정 반영
+          sudo cp /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx/default.conf /etc/nginx/conf.d/default.conf
+
+          # 설정 검증 및 롤백 처리
+          if sudo nginx -t; then
+            sudo nginx -s reload
+          else
+            echo "[ERROR] nginx -t failed. Rolling back..."
+            if [ -f /etc/nginx/conf.d/default.conf.bak ]; then
+              sudo mv /etc/nginx/conf.d/default.conf.bak /etc/nginx/conf.d/default.conf
+              sudo nginx -t && sudo nginx -s reload || true
+            fi
+            exit 1
+          fi
+
+          # 애플리케이션 재배포
+          cd /home/${{ secrets.USERNAME }}/solid-connect-server
+          docker compose -f docker-compose.prod.yml down
+          docker compose -f docker-compose.prod.yml up -d --build

95-95: 3) YAML Lint 경고(trailing spaces) 제거
라인 95의 공백만 있는 줄을 정리해 Lint를 통과시키세요.

적용 diff:

-          
+
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6537bd3 and 27404b7.

📒 Files selected for processing (4)
  • .github/workflows/dev-cd.yml (1 hunks)
  • .github/workflows/prod-cd.yml (1 hunks)
  • docs/infra-config/nginx.dev.conf (3 hunks)
  • docs/infra-config/nginx.prod.conf (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/dev-cd.yml
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/prod-cd.yml

[error] 95-95: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
docs/infra-config/nginx.dev.conf (1)

21-24: 4) stage 인증서 경로와 도메인 매칭을 최종 확인하세요.
도메인(api.stage.solid-connection.com)과 live 경로의 인증서/키가 실제 발급·갱신 상태와 일치하는지 점검이 필요합니다.

  • 점검 체크리스트
      1. /etc/letsencrypt/live/api.stage.solid-connection.com/{fullchain.pem,privkey.pem} 심볼릭 링크가 유효한지 확인.
      1. nginx -t 통과 후 nginx -s reload 시 에러 로그에 인증서 관련 경고가 없는지 확인.
      1. 체인 누락 방지를 위해 브라우저/CLI(예: openssl s_client -connect ... -servername ...)로 완전한 체인이 노출되는지 확인.
docs/infra-config/nginx.prod.conf (1)

13-15: 4) 운영 인증서 경로와 SNI 일치 여부를 배포 전에 점검하세요.
인증서 파일 권한, 체인, SNI(server_name) 불일치가 흔한 장애 요인입니다.

  • 점검 체크리스트
      1. live 경로 심볼릭 링크 유효성.
      1. ssl_stapling on 사용 시 resolver가 전역에 선언되어 있는지(없다면 /etc/nginx/nginx.conf http 블록에 resolver 추가 권장).
      1. openssl s_client -connect api.solid-connection.com:443 -servername api.solid-connection.com -showcerts로 체인 검사.

@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from 27404b7 to e8ad57a Compare August 26, 2025 02:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
.github/workflows/prod-cd.yml (2)

74-83: nginx 설정 파일 전송 단계: 보안·재현성·디렉터리 보장 개선 제안

1) 액션 버전 고정이 필요합니다. master 브랜치 추적은 서플라이체인 리스크를 키웁니다. 태그 또는 커밋 SHA로 고정해 주세요.  
2) 원격 디렉터리 생성 보장이 필요합니다. scp는 목적지 디렉터리가 없으면 실패하므로 사전 SSH 단계에서 mkdir -p로 생성해 주세요.  
3) host_fingerprint 지정이 권장됩니다. MITM 방지를 위해 scp-action/ssh-action에 호스트 지문을 설정하세요.  
4) 디렉터리 네이밍 일관성을 검토해 주세요. prod는 앱 파일은 solid-connect-server, nginx 설정은 solid-connection-prod 경로를 사용합니다. 의도된 분리라면 README/런북에 목적을 명시해 주세요.

적용 예시(디렉터리 보장 + 호스트 지문) diff:

-    - name: Copy nginx config to remote
-      uses: appleboy/scp-action@master
+    - name: Ensure remote nginx dir exists
+      uses: appleboy/ssh-action@master
+      with:
+        host: ${{ secrets.HOST }}
+        username: ${{ secrets.USERNAME }}
+        key: ${{ secrets.PRIVATE_KEY }}
+        script_stop: true
+        script: |
+          mkdir -p /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx
+
+    - name: Copy nginx config to remote
+      uses: appleboy/scp-action@master
       with:
         host: ${{ secrets.HOST }}
         username: ${{ secrets.USERNAME }}
         key: ${{ secrets.PRIVATE_KEY }}
+        host_fingerprint: ${{ secrets.PROD_HOST_FINGERPRINT }}
         source: "./docs/infra-config/nginx.prod.conf"
         target: "/home/${{ secrets.USERNAME }}/solid-connection-prod/nginx"
         rename: "default.conf"

92-98: nginx 적용 순서 좋습니다. 한 줄 공백 트레일링 스페이스 제거와 실패 시 백업 보강을 제안합니다.

1) 라인 95의 트레일링 스페이스를 제거해 YAMLLint 경고를 없애 주세요.  
2) 적용 실패 시 복구를 위해 현재 default.conf 백업 후 재적용·성공 시 백업 삭제를 권장합니다.  
3) 스크립트 상단에 set -Eeuo pipefail을 추가하면 미묘한 실패도 즉시 중단되어 안전합니다.

제안 diff:

         script: |
-          sudo cp /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx/default.conf /etc/nginx/conf.d/default.conf
-          sudo nginx -t
-          sudo nginx -s reload
-          
+          set -Eeuo pipefail
+          # backup current conf
+          sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak || true
+          # stage new conf
+          sudo cp /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx/default.conf /etc/nginx/conf.d/default.conf
+          # validate and reload
+          sudo nginx -t
+          sudo nginx -s reload
+          # cleanup backup after successful reload
+          sudo rm -f /etc/nginx/conf.d/default.conf.bak
           
           cd /home/${{ secrets.USERNAME }}/solid-connect-server
           docker compose -f docker-compose.prod.yml down
           docker compose -f docker-compose.prod.yml up -d --build
.github/workflows/dev-cd.yml (2)

74-83: DEV nginx 설정 업로드 단계: 실패 여지 최소화를 위한 가드 추가 제안

1) master 고정 대신 태그/커밋 SHA로 버전 고정이 필요합니다. 재현성과 보안을 동시에 확보합니다.  
2) 업로드 대상 디렉터리 보장을 위해 사전 mkdir -p 단계를 추가해 주세요. 없으면 scp가 실패합니다.  
3) host_fingerprint를 설정하면 호스트 키 검증이 강제되어 안전합니다.

예시 diff:

-      - name: Copy nginx config to remote
-        uses: appleboy/scp-action@master
+      - name: Ensure remote nginx dir exists
+        uses: appleboy/ssh-action@master
+        with:
+          host: ${{ secrets.DEV_HOST }}
+          username: ${{ secrets.DEV_USERNAME }}
+          key: ${{ secrets.DEV_PRIVATE_KEY }}
+          script_stop: true
+          script: |
+            mkdir -p /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx
+
+      - name: Copy nginx config to remote
+        uses: appleboy/scp-action@master
         with:
           host: ${{ secrets.DEV_HOST }}
           username: ${{ secrets.DEV_USERNAME }}
           key: ${{ secrets.DEV_PRIVATE_KEY }}
+          host_fingerprint: ${{ secrets.DEV_HOST_FINGERPRINT }}
           source: "./docs/infra-config/nginx.dev.conf"
           target: "/home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx"
           rename: "default.conf"

92-98: CI 스크립트 견고화 및 WebSocket 핸드셰이크 검증 추가

아래 네 가지 변경을 제안드립니다:

  1. docker compose -f docker-compose.dev.yml 일관성 유지
    현재 상태가 적절하므로 변경 불필요

  2. set -Eeuo pipefail 추가
    CI 스크립트 상단에 삽입해 작은 오류도 즉시 감지할 수 있도록 개선

  3. WebSocket 핸드셰이크 지시어 검증 강화
    스크립트 실행 결과 proxy_set_header Connection 헤더와 map $http_upgrade $connection_upgrade 지시어가 누락된 것으로 확인되었습니다
    해당 지시어가 누락될 경우 경고 메시지를 출력하도록 자동 검증 스크립트에 반영해주세요

  4. 테스트 코드 ws/wss 스킴 탐지 스크립트 확장
    현재 test 디렉토리에서 ws/wss/http 패턴이 미발견되었고, 특히 Kotlin(.kt) 파일 타입이 미인식된 상태입니다
    Java(.java)뿐만 아니라 Kotlin(.kt)까지 포함해 스킴 사용 여부를 점검하도록 업데이트해주세요

제안된 diff:

         script: |
-            sudo cp /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default.conf /etc/nginx/conf.d/default.conf
-            sudo nginx -t
-            sudo nginx -s reload
+            set -Eeuo pipefail
+            sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak || true
+            sudo cp /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default.conf /etc/nginx/conf.d/default.conf
+            sudo nginx -t
+            sudo nginx -s reload
+            sudo rm -f /etc/nginx/conf.d/default.conf.bak
 
             cd /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev
             docker compose -f docker-compose.dev.yml down
             docker compose -f docker-compose.dev.yml up -d --build

검증 스크립트 예시:

#!/bin/bash
# 1) WebSocket 핸드셰이크 지시어 확인
rg -n 'proxy_http_version\s+1\.1' docs/infra-config/nginx.*.conf || echo "proxy_http_version 1.1 누락 가능성"
rg -n 'proxy_set_header\s+Upgrade\s+\$http_upgrade' docs/infra-config/nginx.*.conf || echo "Upgrade 헤더 누락 가능성"
rg -n 'proxy_set_header\s+Connection\s+(upgrade|\$connection_upgrade)' docs/infra-config/nginx.*.conf || echo "Connection 헤더 누락 가능성"
rg -n 'map\s+\$http_upgrade\s+\$connection_upgrade' docs/infra-config/nginx.*.conf || echo "map \$http_upgrade \$connection_upgrade 누락 가능성"

# 2) 테스트 코드 내 ws/wss 스킴 탐지(Java .java + Kotlin .kt)
rg -n -C2 -i 'wss?://|http://|https://' --type=java --type-add 'kt:*.kt' --type=kt test || echo "ws/wss 스킴 미발견 가능성"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 27404b7 and e8ad57a.

📒 Files selected for processing (4)
  • .github/workflows/dev-cd.yml (1 hunks)
  • .github/workflows/prod-cd.yml (1 hunks)
  • docs/infra-config/nginx.dev.conf (3 hunks)
  • docs/infra-config/nginx.prod.conf (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/infra-config/nginx.prod.conf
  • docs/infra-config/nginx.dev.conf
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/prod-cd.yml

[error] 95-95: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 이쪽 지식이 많지 않지만 문제 없어보입니다!
그냥 보면서 신경 쓰인건
dev 는 localhost:8080
prod는 127.0.0.1:8080
차이밖에 없습니다 ㅋㅋ..

- 서버에 존재하는 default를 nginx.conf에 통합
- 초기 핸드셰이크는 ws가 아니라 http 프로토콜을 사용함
- 핸드셰이크를 테스트하므로 목적에 맞게 클래스 이름 변경
- docker compose down 시 명시적으로 yml 파일 지정
@whqtker whqtker force-pushed the fix/480-websocket-nginx-conf branch from e8ad57a to 628e7a6 Compare September 18, 2025 14:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
.github/workflows/dev-cd.yml (1)

74-83: 1) 파일명 불일치 이슈가 깔끔히 해결되었어요.

  • default → default.conf 오타가 바로잡혔고, 홈 디렉터리 경유 후 권한 상승 복사 패턴이 안전해졌어요. 좋습니다.
🧹 Nitpick comments (6)
.github/workflows/prod-cd.yml (3)

74-83: 1) 원격 경로 생성 보강으로 배포 실패를 선제 차단해요.

  • 대상 디렉터리(/home/${{ secrets.USERNAME }}/solid-connection-prod/nginx)가 없으면 scp 단계가 실패할 수 있어요. 한 줄로 미리 생성하면 안전해요.

  • 제안 변경사항.

    • 사전 준비 단계(ssh-action)로 원격 디렉터리를 생성해요.
+    - name: Prepare remote nginx dir (prod)
+      uses: appleboy/ssh-action@master
+      with:
+        host: ${{ secrets.HOST }}
+        username: ${{ secrets.USERNAME }}
+        key: ${{ secrets.PRIVATE_KEY }}
+        script_stop: true
+        script: |
+          mkdir -p /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx

84-98: 2) nginx 적용을 더 안전하고 무중단에 가깝게 만들어요.

  • 검증 전 임시 파일로 설치하고, 테스트 통과 시 원자적으로 교체하면 리스크가 줄어요. 한편 docker compose는 down 없이 up -d --build로도 갱신돼요.

  • 제안 변경사항.

    • set -Eeuo pipefail로 스크립트 안정성을 높여요.
    • sudo install로 권한·모드를 명시하고 .new로 배치 후 nginx -t 통과 시 mv로 교체해요.
    • 불필요한 down을 제거해 다운타임을 줄여요.
-          sudo cp /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx/default.conf /etc/nginx/conf.d/default.conf
-          sudo nginx -t
-          sudo nginx -s reload
-          
-          cd /home/${{ secrets.USERNAME }}/solid-connect-server
-          docker compose -f docker-compose.prod.yml down
-          docker compose -f docker-compose.prod.yml up -d --build
+          set -Eeuo pipefail
+          tmp=/etc/nginx/conf.d/default.conf.new
+          sudo install -m 0644 /home/${{ secrets.USERNAME }}/solid-connection-prod/nginx/default.conf "$tmp"
+          sudo nginx -t
+          sudo mv "$tmp" /etc/nginx/conf.d/default.conf
+          sudo nginx -s reload
+
+          cd /home/${{ secrets.USERNAME }}/solid-connect-server
+          docker compose -f docker-compose.prod.yml up -d --build --remove-orphans

84-98: 액션 버전 고정: GitHub Actions를 릴리스 태그 또는 커밋 SHA로 고정하세요.
@master 참조는 예기치 않은 브레이킹 변경에 취약합니다.
파일: .github/workflows/prod-cd.yml (라인 84–98)

  1. appleboy/ssh-action 버전 고정.
    권장 태그: appleboy/ssh-action@v1.2.2 — 릴리스 2025-03-09.

  2. appleboy/scp-action 버전 고정.
    권장 태그: appleboy/scp-action@v1.0.0 — 릴리스 2024-04-27.

가능하면 릴리스 태그 대신 커밋 SHA로 고정하면 더 안전합니다.

.github/workflows/dev-cd.yml (3)

74-83: 2) 원격 경로 생성 한 줄로 배포 탄탄하게 만들기.

  • dev 환경도 대상 디렉터리가 없으면 scp가 실패해요. mkdir -p를 선행해요.

  • 제안 변경사항.

    • prod와 동일하게 사전 준비 단계 추가를 권장해요.
+      - name: Prepare remote nginx dir (dev)
+        uses: appleboy/ssh-action@master
+        with:
+          host: ${{ secrets.DEV_HOST }}
+          username: ${{ secrets.DEV_USERNAME }}
+          key: ${{ secrets.DEV_PRIVATE_KEY }}
+          script_stop: true
+          script: |
+            mkdir -p /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx

84-98: 3) nginx 교체 절차 원자화 + 다운타임 축소를 제안해요.

  • 임시 파일 배치 후 테스트·원자 교체로 안전성을 높이고, compose down 생략으로 가용성을 높여요.

  • 제안 변경사항.

    • set -Eeuo pipefail, sudo install, 임시 파일 사용, down 제거를 적용해요.
-            sudo cp /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default.conf /etc/nginx/conf.d/default.conf
-            sudo nginx -t
-            sudo nginx -s reload
-
-            cd /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev
-            docker compose -f docker-compose.dev.yml down
-            docker compose -f docker-compose.dev.yml up -d --build
+            set -Eeuo pipefail
+            tmp=/etc/nginx/conf.d/default.conf.new
+            sudo install -m 0644 /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev/nginx/default.conf "$tmp"
+            sudo nginx -t
+            sudo mv "$tmp" /etc/nginx/conf.d/default.conf
+            sudo nginx -s reload
+
+            cd /home/${{ secrets.DEV_USERNAME }}/solid-connection-dev
+            docker compose -f docker-compose.dev.yml up -d --build --remove-orphans

84-98: 액션 태그 고정 및 워크플로 동시 실행 제어 적용 권장
웹 검증: appleboy/ssh-action 최신 안정 릴리스 태그는 v1.2.2이고 appleboy/scp-action은 v1.0.0입니다.

  1. 액션 버전 고정.
    .github/workflows/dev-cd.yml에서 uses: appleboy/ssh-action@masteruses: appleboy/ssh-action@v1.2.2로 고정하세요.
    다른 워크플로에서 scp-action을 사용 중이면 appleboy/scp-action@v1.0.0로 고정하세요.

  2. 워크플로 동시 실행 제어 추가.
    파일 상단에 concurrency 섹션을 추가해 동일 브랜치의 중복 실행을 방지하세요.

제안된 diff (동일 브랜치 중복 실행 방지용, 원본과 동일):

name: "[DEV] Build Gradle and Deploy"
 
on:
  push:
    branches: [ "develop" ]
  workflow_dispatch:
 
+concurrency:
+  group: dev-deploy-${{ github.ref }}
+  cancel-in-progress: true

위치: .github/workflows/dev-cd.yml (Lines 84-98)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8ad57a and 628e7a6.

📒 Files selected for processing (5)
  • .github/workflows/dev-cd.yml (1 hunks)
  • .github/workflows/prod-cd.yml (1 hunks)
  • docs/infra-config/nginx.dev.conf (3 hunks)
  • docs/infra-config/nginx.prod.conf (1 hunks)
  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/test/java/com/example/solidconnection/websocket/WebSocketHandshakeTest.java
  • docs/infra-config/nginx.dev.conf
  • docs/infra-config/nginx.prod.conf
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

@whqtker whqtker merged commit 1cd50d4 into solid-connection:develop Sep 18, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

버그 Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: WebSocket Handshake 실패 문제 해결

3 participants