이번주에는 Nginx 리버스 프록시 기능 구현을 했지만 아직 완료하진 못했고 오류 해결 중에 있습니다..
제목에서 보시다시피 한 기능을 위해서 삽집을 일주일 동안 했는데요.. 아직 해결을 못했다는게 하하...
다행히도 문제의 정확한 원인을 파악했기 때문에, 현재 해결하지 못한 에러에 대해선 다음 블로그에 공유해 드릴 수 있을 것 같습니다!
'원인을 찾았다면 왜 바로 해결하지 않았나?'라는 의문이 드실 수도 있을 것 같습니다. 사실 다음 주 계획이 HTTPS 및 Nginx SSL 설정을 하려고 했었는데요. 현재 발생한 문제가 HTTPS 통신을 적용하면 자연스럽게 해결될 것 같아서, 시간 관계상 다음 주 작업과 함께 진행하기로 결정했습니다.
제가 Nginx 학습하면서 블로그를 작성했는데 해당 링크를 첨부해보도록 하겠습니다!
https://young-code.tistory.com/1
🚩프로젝트 과정 (2주차)
1. 문제 상황
💡카카오 소셜 로그인 플로우
1. 클라이언트가 카카오 로그인 버튼 클릭
2. 클라이언트는 카카오 인증 서버로 리다이렉트 (카카오 로그인 페이지)
3. 사용자가 카카오 로그인 완료 후, 카카오는 설정된 리다이렉트 URI로 인가 코드(authorization code)를 전달
4. 백엔드 서버는 인가 코드를 사용하여 카카오 서버에 액세스 토큰 요청
5. 카카오 서버가 백엔드에 액세스 토큰 발급
6. 백엔드는 카카오로부터 받은 액세스 토큰으로 사용자 정보 요청
7. 백엔드 서버는 받은 사용자 정보로 회원가입/로그인 처리 후 Refresh Token을 생성하여 쿠키에 담아 클라이언트에게 전달
8. 클라이언트는 받은 Refresh Token으로 백엔드 서버에 Access Token 생성을 요청
9. 백엔드 서버는 전달받은 Refresh Token을 검증한 후 Access Token을 생성하여 응답 헤더에 담아 클라이언트에게 전달
10. 클라이언트는 전달받은 Access Token을 사용하여 이후 사용자 인증이 필요한 요청을 처리
현재 서버 아키텍처는 보안을 고려하여 Frontend, Backend, AI, RDS 서버를 동일한 VPC 내의 Private Subnet에 위치시켰으며, 외부 접근을 위한 Nginx는 Public Subnet에 배치한 상태입니다.
이러한 구조에서 Nginx 리버스 프록시를 통한 카카오 소셜 로그인(OAuth2&JWT) 구현 과정에서 여러 기술적 이슈들이 있었습니다. 구체적으로는 Proxy 설정 오류, Redirect URI 불일치, Cookie 전달 이슈, 소켓 문제 등 다양한 에러가 발생했는데요.
지금부터 이러한 문제들을 어떤 과정으로 해결해 나갔는지 상세히 공유해 보도록 하겠습니다!
2. 문제 해결
- Reverse Proxy 설정 오류 (Location 블록)
프론트엔드와 백엔드 서버를 구동하고 Nginx을 통한 Reverse Proxy 설정 중 문제가 발생했습니다. 초기에는 Reverse Proxy가 제대로 설정이 되었는지 확인하기 위해 서버 간 통신은 약간 미루고 백엔드의 Swagger가 정상적으로 동작하는지 테스트해 보았습니다. 결과는..
Swagger 페이지에 접근할 수 없었습니다. 그래서 브라우저에서 직접 http://[IP주소]/swagger-ui/index.html에 접속해 보니 api-docs/swagger-config에 대해서 500 에러가 발생하더라구요.
오류 발생 당시의 nginx.conf 파일 내용입니다.
server {
listen 8080;
server_name 3.38.27.190;
location /api {
proxy_pass http://10.0.1.44:8000;
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;
}
location /oauth2 {
proxy_pass http://3.38.27.190:8080/oauth2/authorization/kakao;
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;
}
location /swagger-ui {
proxy_pass http://10.0.1.44:8080/swagger-ui/index.html;
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;
}
}
/swagger-ui에 대한 프록시 설정을 했음에도 계속 안 돼서 이것저것 시도를 해봤습니다.
삽질 시작..🛠️
서칭을 해보니 백엔드 엔드포인트를 /api로 설정해 두었는데 Swagger 오류가 발생한 엔드포인트는 /api-docs였습니다. Nginx는 프록시 설정 시 가장 일치하는 경로를 우선적으로 선택하기 때문에, Swagger 페이지임에도 불구하고 location /api 설정으로 처리되어 리버스 프록시가 작동되는 것이었습니다. 그래서 500 에러가..
아래의 코드를 추가하였더니 정상적으로 Swagger 페이지가 작동되었습니다!
location /api-docs {
proxy_pass http://10.0.1.44: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;
}
- Redirect URI 불일치
Kakao Developers에서 로그인 Redirect URI을 설정했기 때문에 카카오 로그인 UI에 잘 들어갈 수 있었지만 로그인 후 리다이렉트가 이상한 곳으로 되는 것을 확인할 수 있었습니다.
백엔드 개발자와 얘기를 나눠보니 Redirect URI도 리버스 프록시를 해야 한다라고 피드백을 받았고, 아래의 코드를 nginx.conf 파일에 추가했습니다.
location /login/oauth2/code/kakao {
proxy_pass http://10.0.1.44:8080/login/oauth2/code/kakao;
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;
}
💡Redirect URI
인증 성공 후, 카카오에서 애플리케이션으로 돌아올 주소로 설정 URI
이는 카카오가 인증을 완료한 뒤 리다이렉션할 경로로, 사용자가 로그인하면 Spring Security가 이 URI로 사용자 정보를 받아와 처리
(이번 프로젝트에서는 Nginx을 통해 백엔드 서버로 접근하고 있기 때문에 모든 설정을 Nginx IP로 설정해줬습니다!)
- 프론트엔드 로컬 + 백엔드 배포 환경에서의 개발 환경 구축
이전에 프론트엔드와 백엔드를 서로 로컬에서만 테스트하고, 배포환경에서 테스트를 한번도 하지 않았던 적이 있습니다.
그 결과, 마지막에 배포 환경에서 두 서버가 제대로 연결되지 않는 아픈 기억이..🥲
이 경험을 통해, 프론트엔드 서버는 배포 환경이나 로컬 환경이나 동일하게 동작하므로, '백엔드 서버만 미리 배포하고 하나의 기능이 완성될 때마다 연결을 테스트를 해야겠다' 라는 회고를 한적이 있습니다.
이번에 프론트엔드 개발자 분께서 백엔드는 배포된 상태, 프론트는 로컬에서 연결 테스트가 가능하냐고 물으셨고, 저는 가능하다고 답한 후 시도해 보았습니다. 로컬에서는 문제없이 연결이 되었지만, 백엔드만 배포된 상태에서는 연결이 되지 않았습니다.
처음에는 Nginx 설정 문제라고 생각하여 nginx.conf 파일을 확인하고 로그를 보며 테스트를 해봤지만, 프록시 설정은 올바르게 작동하고 있었습니다. 문제를 추적해 보니, 백엔드에서 Redirect URI을 localhost/redirect로 설정하고 있던 것이 원인이었습니다.
프론트엔드가 로컬 환경에서 실행되기 때문에 Redirect URI을 localhost로 해야한다고 생각했지만, 백엔드 서버 기준의 localhost와 프론트엔드 기준의 localhost는 다르다는 점을 알게 되었습니다. 프론트엔드 클라이언트의 IP는 유동적으로 변하기 때문에, 백엔드에서 이를 동적으로 받아야 했던 것이었죠. 결국, 백엔드 서버가 클라이언트 IP를 유동적으로 받을 수 있도록 로직을 추가했고, 이를 통해 로컬에서 요청과 응답이 정상적으로 이루어지는 환경을 구축할 수 있었습니다.
- Cookie가 백엔드 서버에서 클라이언트로 전달되지 않는 문제
localhost 환경에서 HTTP 통신을 통해 백엔드에서 생성한 Refresh Token이 Cookie에 잘 담겨 클라이언트로 전달되는 것을 확인하고, 배포된 환경에서 Nginx 리버스 프록시로 가 잘 전달이 되는지 테스트를 해보았습니다.
같은 HTTP 통신임에도 불구하고 쿠키가 백엔드 서버에서 브라우저로 전달되지 않는 문제가 발생하고 있었습니다.
nginx.conf 파일에 SameSite=None을 설정과 경로 설정을 추가했더니 잘 전달 확인이 가능했습니다!
location /redirect {
proxy_pass http://3.37.229.132:3000;
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_pass_header Set-Cookie;
proxy_cookie_path / "/; SameSite=None"; // 추가한 코드
}
- 브라우저가 Refresh Token을 쿠키에 담아서 백엔드 요청을 보내지 않는 문제
저는 처음에 IP가 다르기 때문에 쿠키가 전달되지 않는다고 생각하여, Nginx에서 쿠키를 전달할 때 Samesite=None을 설정하고 시도해 보았습니다. 역시 여전히 쿠키가 전달되지 않는 것을 확인할 수 있었고, '왜 동일한 HTTP 통신에서 로컬 환경에서는 쿠키 전달되는데 배포 환경에서는 안될까?'라는 의문이 들었습니다.
또다시 삽질 시작..🛠️
브라우저 정책에 대해 알게 되었고, 대부분의 브라우저에서는 로컬 환경에서 HTTP여도 secure 속성을 무시하고 쿠키가 전달된다는 사실..!
또한, Samesite = None일 경우, secure 설정은 필수이기 때문에 Samesite=None일 경우 반드시 HTTPS 통신을 해야 쿠키가 정상적으로 전달된다는 점을 알게 되었습니다.
이런 에러가 있었기 때문에, 블로그 초반에 다음 주에 HTTPS 설정을 마무리하면 Nginx을 통한 설정을 완료하고 통신이 정상적으로 이루어질 것이라고 말한 것입니다!
이 에러에 대해선 다음 블로그에서 다시 언급하겠습니다! (그때는 아마 해결이 되겠죠..?🙏)
- 로그 커스텀
추가로 Nginx 로그에서 브라우저가 Nginx로 쿠키를 제대로 전달하는지 확인할 수 없는 게 너무 답답했기 때문에, 로그를 아래와 같이 커스텀하여 쿠키가 제대로 전달되는지 확인하면서 오류 해결을 진행했습니다.
로그를 커스텀해서 확인하니 속도도 빨라지고 쿠키가 제대로 전달되는지 명확해지더라고요! 다음 에러가 발생하면 처음부터 로그를 커스텀하여 진행할 생각입니다!
log_format custom '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_cookie" "$sent_http_set_cookie" '
'"$http_authorization"';
access_log /var/log/nginx/access.log custom;
💡추가 오류 (React)
브라우저에서 백엔드로 첫 번째 요청을 보낼 때, 웹 소켓을 사용하지 않았음에도 불구하고 '웹 소켓 실패'라는 오류가 발생했습니다. 이를 React에서 veto로 수정하여 웹 소켓을 제어함으로써 오류를 해결할 수 있었습니다.
Nginx를 중간에 껴서 발생하는 에러인 줄 알고 여기에 엄청난 에너지 소모를 했습니다..ㅎ
'Project > Devita' 카테고리의 다른 글
[Devita] Domain & SSL 설정 (4) | 2024.11.14 |
---|---|
[Devita] 인프라 아키텍처 & CI/CD 파이프라인 (1) | 2024.11.06 |