먼 옛날 옛날
최근 회사에서 스프링 기반의 애플리케이션 이외의 Headless cms 기반의 애플리케이션에 대해 고민하기 시작하며 회사 개발자 중 유일하게 node.js를 (사용해봤다고 말하는 게 민망하긴 하다만...) 사용해 본 경험이 있는 나를 자연스레 담당 개발자로 두게 되었다. 아직 Headless에 대해 분명하고 명확하게 이해하고 있는 부분은 아니나, 내가 알고 있는 Headless의 요점은 백엔드와 프론트엔드의 분리, 즉 내부 서버 로직이 특정 뷰에 종속되지 않고 api를 이용해 다양한 형태의 클라이언트에게 서비스를 제공할 수 있는 부분이었다.
나는 어디까지나 자바 개발자였기 때문에 node.js 생태계에 대한 이해가 넓지 않았고, 그런 우리가 선택한 서버 구축용 프레임워크가 있었으니, 바로 strapi였다. (strapi는 기본적인 crud 코드를 자동 생성해주는 서버 프레임워크이다.)
발단
회사의 Headless cms 구축의 기조는 다음과 같이 진행되었다. 첫 번째, 백엔드 팀은 strapi 오픈소스를 이용한 백엔드 서버를 구축한다. 그리고 두 번째, 병렬적으로 프론트엔드 팀은 next.js를 이용한 프론트엔드 서버를 구축한다.
최근 다른 업무로 바빠 Headless에 손을 도통 두지 못했는데 사내 슬랙으로 프론트엔드 팀으로부터 연락 하나가 왔다. 프론트 서버에서 데이터를 strapi 서버로 전송하려고 하는데 에러가 난다는 것이었다. 에러는 다음과 같았다.
Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure resource '<URL>'. This request has been blocked; the content must be served over HTTPS.
HTTPS....? 뭔가 무서워 보이는 에러였다.
문제 파악
문제의 원인은 그러나 명확해졌다. 에러 메시지가 워낙 친절했기 때문에, 아마도..? 하는 마음에 프론트엔드 서버의 url 스키마를 살펴봤다. 통신을 하고자 하는 양 서버의 통신 프로토콜이 다를 것 같다는 생각이 들었기 때문이었다. 아니나 다를까 프론트엔드 서버의 스키마는 https였다. 이번에는 스트라피 서버의 url 스키마를 살펴 봤다. ssl을 달아 준 기억이 없으니, 당연히 http였다.
The content must be served over HTTPS 라는 에러 메시지에 초점을 두고 strapi 서버에 ssl을 다는 부분에 중점을 뒀다. 그리고 strapi 공식문서를 살펴 보았는데, 그곳에서 nginx라는 처음 보는 용어를 만나게 되었다.
Nginx..?
알고 보니 Nginx는 아파치와 같은 웹서버였다. strapi는 보안 상의 이유, 그리고 여러 기능적 이점으로 strapi를 단독으로 사용하지 않고 nginx 등의 웹서버를 앞단에 배치해 사용하는 것을 권장하고 있었다. (권장이 아니라 강제.. 적인 느낌이 드는 게 strapi 자체에 ssl을 다는 것을 금지하고 있었다. nginx와 같은 웹서버에 ssl을 다는 것을 '권장'강제하고 있더라.)
처음 보는 개념이라 공부를 하는 등 한참 시간을 보내기는 했지만 결국에는 모든게 명확해졌다. strapi 서버는 서버 컴퓨터 뒷단에, nginx는 서버 컴퓨터 앞단에 배치해 모든 클라이언트의 요청이 우선 nginx를 타도록 만드는 것이었다. 이런 개념(웹서버를 앞단에 두고, 실제 로직이 있는 서버 등을 뒷단에 배치하는 개념)을 '리버스 프록시'라고 한다는 것도 처음 알았다.
진행
신들린 구글링으로 수십여개의 블로그 글을 섭렵할 때 쯤, ssh를 이용해 strapi 개발서버가 들어 있는 리눅스 서버에 접근해 nginx를 설치했다. 설치가 완료되자 strapi 공식 문서에 적혀 있는대로 nginx와 strapi 설정을 맞춰 주었다. 이제 브라우저를 켜고, 80번 포트로 맞춰져 있는 서버에 접근하면 성공인데...
... 어, 안된다.
문제
nginx는 리눅스 상에서 일반적으로 /etc/nginx 디렉토리 내에 위치하고 기본 설정 파일은 /etc/nginx/nginx.conf 이다. 나는 nginx로 들어 오는 모든 요청에 대해 strapi 서버가 응대할 수 있도록 아래와 같이 설정을 잡아 주었는데,
...
server {
listen 443 ssl;
server_name _;
# ssl 인증서
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
# strapi 서버 - http://127.0.0.1:1337
# / 이하 경로의 모든 요청을 proxy_pass에 있는 경로로 대신한다.
proxy_pass http://127.0.0.1:1337;
}
}
실제 브라우저에서 요청을 해보니 502 Bad Gateway 에러가 난 것을 확인하게 되었다.
해결
출근 이후 꼬박 8시간을 들여다 보았으나 뾰족한 원인이 보이지 않았다. 설정을 잘못 잡은걸까, 싶어 윈도우 컴퓨터에서도 시도해 보았는데 윈도우에서는 또 정상적으로 작동했다. 'nginx 502'라는 검색어로 검색을 해도 도무지 문제의 원인이 보이지 않아 미치고 환장할 즈음, 문득 머리 속에 nginx 에러 관련 로그가 있지 않을까, 하는 생각이 들었다. 문제를 들여다 본지 꼬박 22시간 이후인 다음 날 새벽 6시의 일이었다. (자다가 에러 로그가 있지 않을까 하는 생각에 잠에서 깼다.)
다행히도 nginx.conf에는 access_log와 error_log 경로가 적혀 있었고, 이곳에 적혀 있는 에러로그 파일 경로를 따라 에러로그를 들여다 볼 수 있게 되었다. 그리고 그곳에서 permision denied라는 에러 메시지를 발견했고 추가적인 웹 서칭을 통해, 아래와 같은 권한 관련 조치를 할 수 있었다.
/etc/nginx/nginx.conf
# user nginx; # 기존 user가 nginx로 되어 있던 부분을
user root; # root로 변경해주었다.
명령어
setsebool -P httpd_can_network_connect on
- nginx 사용자를 기존 'nginx'에서 'root'로 전환하고 (이 단계에서는 해결되지 않음)
- 리눅스 터미널에서 setsebool -P httpd_can_network_connect on 명령어를 사용해주었다.
그러자, 해결이 되었다. 만세.
이후 openssl을 통해 인증서를 내려 받고 strapi를 프록시 하는 nginx에 ssl을 장착했다. 어느덧 근무시간이 되었고, 나는 프론트엔드 팀에 테스트를 요청했다. 두근두근 거리는 10여 분의 시간이 흐른 후,
"잘 작동합니다!"
라는 피드백을 받을 수 있었다.
'메모 & 삽질기록보관소' 카테고리의 다른 글
MQTT 공부 중 참고할만한 문헌 모아두기 (5) | 2023.06.11 |
---|---|
[데이터베이스] MySQL과 MariaDB의 차이점? (0) | 2022.11.17 |
[JPA] 스프링이 엔티티를 인식하지 않는 것 같습니다! (0) | 2022.06.18 |
[윈도우 11?] Mybatis - Password Not Matched!!! (0) | 2022.03.18 |
[jqeury] ajax에서의 parse error | 스프링과 자바스크립트의 부조화 (0) | 2022.03.13 |