CloudFront부터 Nginx까지 가게 된 과정 - https 만들기

Ethan·2026년 2월 14일

이력서와 포트폴리오를 수정하며 다시 입사 지원을 시작하면서, 문득 내 aimtest가 여전히 http를 사용중이라는 것을 깨달았다.

분명 나중에 https로 다시 바꾸려고 했고, 그리 어렵지 않은 작업으로 알고 있었기에 잽싸게 시작했으나,

CloudFront 설정 및 배포부터 해서 ec2에 빌드를 수 번씩 다시 하고, 결국 Nginx를 적용하고 이후에도 재수정을 거치는 험난한 여정의 끝에서야 간신히

https를 사용하는 AimTest 사이트를 얻을 수 있었다.

이 글은 그 험난한 과정을 축약해 정리한 기록이다.

1. 문제의 시작

AimTest에 HTTPS 적용을 위해 AWS CloudFront를 도입했다.

CloudFront 적용 자체는 문제없이 완료되었고, 정적 페이지 역시 정상적으로 로드되었다.

그러나 이후 랭킹 페이지에서 다음과 같은 문제가 발생했다.

  • 메인 화면은 정상
  • 랭킹 페이지 진입 시 데이터가 보이지 않음
  • 콘솔에는 map을 적용할 수 없는 데이터를 불러온다는 에러 메시지
  • 네트워크 탭에서는 API 요청이 의도와 다르게 동작 -> index.html을 반환 받고 있음

즉, UI는 살아 있으나 서버 데이터만 사라진 상태였다.


2. 첫 번째 관찰 – API 요청 경로 이상

네트워크 탭을 확인하던 중, API 요청 URL이 다음과 같이 생성되는 것을 확인했다.

/undefined/rankings

의도한 요청은 /api/rankings였기 때문에,

API base URL이 정상적으로 주입되지 않고 있다는 점을 바로 의심했다.

당시 프론트엔드에서는 다음과 같은 방식으로 API URL을 구성하고 있었다.

process.env.REACT_APP_API_BASE_URL

3. 환경변수 문제를 다시 점검하며 확인한 것

CloudFront 적용 이후 API 요청이 비정상적으로 생성되는 현상을 보며,
가장 먼저 확인한 것은 프론트엔드에서 사용하는 API base URL의 생성 방식이었다.

React 환경에서 process.env.REACT_APP_* 변수가 빌드 타임에 치환된다는 점은 이미 알고 있었지만,
Dockerfile 수정 과정에서 해당 변수를 제거한 이후에도
런타임 환경(EC2)의 설정이 간접적으로 영향을 줄 가능성은 없는지를 한 번 더 점검할 필요가 있었다.

특히 이번 이슈는 단순히 코드 레벨 문제가 아니라,

  • Docker 이미지 빌드 시점
  • GitHub Actions를 통한 배포 흐름
  • EC2 런타임 환경
  • CloudFront를 통한 정적 리소스 서빙

이 여러 레이어가 동시에 바뀐 상황이었기 때문에,
기존에 알고 있던 전제가 여전히 유효한지 검증하는 과정이 필요했다.

검토 결과, 문제의 원인은 런타임 환경이나 EC2 설정이 아니라
프론트엔드가 API 서버의 실제 주소를 직접 알고 있어야만 동작하는 구조 자체에 있었다.

이로 인해 환경변수 주입 방식의 문제를 부분적으로 의심했던 초기 가설을 정리하고,

프론트엔드와 서버의 책임을 분리하는 방향으로 구조를 전환하는 것이 근본적인 해결책이라는 결론에 도달했다.


4. 설계 전환 – 프론트가 서버 주소를 알 필요가 있을까?

이 시점에서 접근 방식을 재검토했다.

“프론트엔드가 API 서버의 실제 주소를 알아야 할 이유가 있을까?”

답은 아니오였다.

프론트엔드는:

  • 서버의 위치를 알 필요가 없고
  • 단지 “API를 호출한다”는 역할만 수행하면 된다

이 책임 분리를 명확히 하기 위해

경로 기반 라우팅을 담당하는 레이어를 도입하기로 결정했다.


5. Nginx 도입과 역할 분리

구조를 다음과 같이 재정의했다.

CloudFront
  ↓
Nginx
  ├── /        → frontend (React 정적 파일)
  └── /api/*   → backend (Node.js API)

이 구조에서:

  • 프론트엔드는 /api/* 경로만 호출
  • 서버 주소, 포트, 프로토콜은 인프라에서 관리
  • HTTPS / 도메인 변경에 프론트 코드 영향 없음

프론트 코드 역시 다음과 같이 단순화되었다.

  
// `${process.env.REACT_APP_API_BASE_URL}/rankings`

axios.get('/api/rankings');

6. Nginx 설정 과정에서의 주요 이슈

Nginx를 적용한 이후에도 한 차례 문제가 발생했다.

API 요청은 /api/rankings로 정상적으로 들어왔지만,

서버에서는 404 응답을 반환하고 있었다.

원인을 확인한 결과, Nginx의 proxy_pass 설정 문제였다.

Backend 라우트 구조

app.use('/api/rankings', router);

잘못된 Nginx 설정

location /api/ {proxy_pass http://backend:3001/;
}

이 설정은 /api 경로를 제거한 채 전달한다.

/api/rankings → /rankings

하지만 서버는 /api/rankings만 알고 있기 때문에 404가 발생했다.


7. 최종 해결 – proxy_pass 동작 방식 이해

Nginx의 proxy_pass뒤 슬래시 유무에 따라 경로 처리 방식이 달라진다.

이를 반영해 설정을 다음과 같이 수정했다.

location /api/ {proxy_pass http://backend:3001;
}

이후 요청 흐름은 다음과 같이 정렬되었다.

/api/rankings
→ backend:3001/api/rankings

8. 최종 결과

  • CloudFront + HTTPS 환경에서 정상 동작
  • 프론트엔드에서 서버 주소 의존성 제거
  • 랭킹 API 정상 응답
  • 동일 구조로 로컬/운영 환경 모두 대응 가능

9. 정리하며 – 이번 문제에서 얻은 교훈

이번 이슈를 통해 명확해진 점은 다음과 같다.

  1. 프론트와 서버의 책임을 분리하면 문제의 범위가 급격히 줄어든다
  2. 인프라는 “코드를 덜 바꾸게 만드는 계층”이어야 한다
  3. Nginx의 작은 설정 차이가 전체 시스템 동작을 바꿀 수 있다
  4. 문제 해결의 핵심은 추측이 아니라 관측과 구조적 재검토

마무리

이 문제는 단순히 랭킹 API를 복구하는 작업이 아니라,

웹 서비스가 실제 운영 환경에서 어떻게 결합되어야 하는지를 다시 점검하는 계기였다.

결과적으로 Nginx라는 처음 사용하는 기술까지 도입하게 되었지만,

이 선택은 “문제를 우회한 해결”이 아니라

역할과 책임을 명확히 나눈 구조적 해결이었다고 생각한다.

profile
"Actions speak louder than words"

0개의 댓글