6 분 소요

Axios baseURL 설정 방식

  • 프론트엔드 소스 코드에 baseURL를 설정하는 방식이다.
export const apiClient = axios.create({
  baseURL: 'http://localhost:8080',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json'
  }
})

장점:

  • 구현이 간단하고 직관적
  • 클라이언트 코드에서 API 엔드포인트 관리가 용이
  • 개발 환경에서 빠른 테스트와 디버깅 가능

단점:

  • CORS (Cross-Origin Resource Sharing) 이슈 발생 가능
  • 프로덕션 환경에서 baseURL을 하드코딩하면 유연성이 떨어짐
  • 클라이언트의 보안에 취약할 수 있음 (API 엔드포인트가 노출됨)

Nginx 프록시 설정 방식

  • 프론트엔드 소스 코드에는 백엔드 API를 설정하지 않고, 웹 서버인 nginx에 프록시 기능을 사용하는 방식이다.
  • 예를 들어 브라우저에 http://exam.com/api/v1/users 라는 요청을 하면 클라이언트 소스 코드에서 서버에 데이터를 요청하기 위해 내부적으로 http://api-server/api/v1/users라고 요청을 보내게 된다.
  • 이때 서버에 보낸 요청 URL은 개발자 도구에 노출된다. 그리고 서버로부터 응답이 오면 해당 결과에 따라 새로운 페이지를 호출하는 형태로 진행된다.
  • 하지만 nginx의 프록시 설정을 사용하면 서버로 보내는 요청은 nginx 프록시를 통해 서버에 전달된다. 따라서 개발자 도구에 표시되지 않는다.
export const apiClient = axios.create({
})
  • 백엔드 API 주소는 nginx.conf 파일에 proxy 설정으로 대체한다.
server {
    listen       80;
    server_name  _;
    
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    
    // http://localhost:8080/api/.. 요청이 오면 /api/가 매칭되어 실행되는 프록시 설정
    location /api/ {
        # 마지막 슬래시를 추가하면 location 에 정의한 경로가 무시되어 http://localhost:8080 로 전달된다.
        # 마지막에 슬래시가 없으면 정의한 경로가 유지되어 http://localhost:8080/api/ 로 전달된다. 
        proxy_pass http://localhost:8080/;
    }
    
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

장점:

  • CORS 이슈 해결 (브라우저 요청이 아니기 때문에)
  • 보안 강화 (실제 백엔드 서버 정보 숨김)
  • 로드 밸런싱, 캐싱 등 추가 기능 구현 가능
  • SSL/TLS 종단점 통합 관리 가능
  • 환경별 설정 관리가 용이 (개발/스테이징/프로덕션)

단점:

  • 추가적인 설정과 관리가 필요
  • 프록시 서버 구성에 대한 지식 필요
  • 약간의 성능 오버헤드 발생 가능

실제 프로덕션 환경에서는 대부분 Nginx 프록시 방식을 사용하는 것이 권장됩니다. 이는 보안, 확장성, 유지보수 측면에서 더 많은 이점을 제공하기 때문입니다. 개발 환경에서는 직접 baseURL을 설정하는 방식을 사용하고, 프로덕션 환경에서는 Nginx 프록시를 사용하는 하이브리드 접근방식도 많이 사용됩니다.

기본 프록시 설정

location /api/ {
    proxy_pass http://localhost:8080/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    proxy_set_header Content-Type application/json;
    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;
}

설정 항목별 상세 설명

1. Location 블록

location /api/ {
  • /api/로 시작하는 모든 요청에 대해 아래의 설정들을 적용
  • 끝의 슬래시(/)는 URL 매칭 패턴에 영향을 미침
    • /api/는 정확히 이 경로로 시작하는 요청만 매칭
    • /api는 ‘api’로 시작하는 모든 경로를 매칭

2. Proxy Pass

proxy_pass http://localhost:8080/;
  • 클라이언트 요청을 백엔드 서버로 전달
  • 끝의 슬래시(/)가 URL 재작성에 영향:
    • 있는 경우: location에서 매칭된 부분을 제거하고 나머지 경로만 전달
    • 없는 경우: 전체 URL을 그대로 전달

3. WebSocket 지원 설정

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
  • WebSocket 연결을 지원하기 위한 필수 설정
  • HTTP/1.1 프로토콜 사용 명시
  • WebSocket으로의 프로토콜 업그레이드 지원

4. 프록시 캐시 설정

proxy_cache_bypass $http_upgrade;
  • WebSocket 연결 시 프록시 캐시를 우회
  • 실시간 양방향 통신을 위해 필요

5. 타임아웃 설정

proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
  • proxy_connect_timeout: 백엔드 서버 연결 수립 제한 시간
  • proxy_send_timeout: 백엔드 서버로 요청 전송 제한 시간
  • proxy_read_timeout: 백엔드 서버로부터 응답 대기 제한 시간

6. 프록시 헤더 설정

proxy_set_header Content-Type application/json;
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;
  • Content-Type: JSON 형식의 데이터 전송 명시
  • X-Real-IP: 실제 클라이언트 IP 전달
  • X-Forwarded-For: 클라이언트 및 중간 프록시 서버들의 IP 주소 체인
  • X-Forwarded-Proto: 원본 요청의 프로토콜(http/https) 정보
프록시 헤더 설정과 서버의 RemoteAddr()

우선 전제부터 정리하자면,

  • Host 헤더는 “요청이 어떤 도메인/호스트로 왔는지”를 나타내는 HTTP 헤더이고,
  • request.getRemoteAddr()(서블릿/JSP 등 Java 환경 기준)은 “실제 TCP 연결을 맺은 클라이언트(혹은 프록시)의 IP 주소”를 반환합니다.

즉,

  • proxy_set_header Host $host; 설정 유무는 백엔드 서버가 인식하는 ‘Host 헤더 값’(= 어떤 호스트로 요청이 왔는지) 에 영향을 미치지만,
  • request.getRemoteAddr()가 반환하는 값(= ‘IP 주소’)에는 영향을 주지 않습니다.

왜냐하면 getRemoteAddr()는 결국 서버에 직접 연결된 소켓의 IP를 반환하기 때문입니다. Nginx가 리버스 프록시로 동작할 때는, 백엔드 서버와의 직접 연결은 대부분 “Nginx의 IP”가 됩니다.


1. 기본 동작

1) proxy_set_header 설정이 없을

아무 설정도 없다면,

  • 백엔드 서버가 보는 Host 헤더
    • 기본적으로 Nginx는 proxy_pass http://api-server/; 등의 설정에 기재된 호스트(api-server)를 Host 헤더로 넘깁니다.
    • 즉, 백엔드 입장에서 request.getHeader("Host") 값을 찍어보면 api-server가 됩니다.
  • 백엔드 서버에서 request.getRemoteAddr()
    • 실제 연결은 Nginx(프록시)백엔드 로 맺어지므로, 백엔드 서버 입장에서는 Nginx 서버의 IP가 클라이언트 IP가 됩니다.
    • 따라서 request.getRemoteAddr()Nginx 서버 IP를 반환합니다.
    • 예: 192.168.0.10 (Nginx IP)

정리:

  • Host 헤더: api-server
  • getRemoteAddr(): Nginx IP

2) proxy_set_header Host $host; 설정이 있을

예시:

location /api/ {
    proxy_pass http://api-server/;
    proxy_set_header Host $host;
}
  • $host클라이언트가 원래 요청한 호스트명(예: exam.com 등)을 의미합니다.
  • 이 설정이 있으면, 백엔드 서버는 Host 헤더를 exam.com 으로 넘겨받게 됩니다.

그러나 이 설정만으로는 IP 관련 설정(X-Real-IPX-Forwarded-For)을 건드리지 않았으므로,

  • 백엔드 서버에서 request.getRemoteAddr()
    • 여전히 Nginx와의 TCP 연결이므로, request.getRemoteAddr()Nginx IP를 반환합니다(바뀌지 않음).

정리:

  • Host 헤더: exam.com
  • getRemoteAddr(): 여전히 Nginx IP

2. 만약 “원본 클라이언트 IP”를 받고 싶다면?

위와는 별개로, request.getRemoteAddr()에서 진짜 사용자 IP(클라이언트의 공인 IP 등)를 받고 싶은 경우가 있습니다. 이 때는 Host 헤더가 아니라 X-Forwarded-For(혹은 X-Real-IP) 와 같은 헤더를 Nginx에서 설정해야 합니다.

예시:

location /api/ {
    proxy_pass http://api-server/;
    
    # 원본 Host 그대로
    proxy_set_header Host $host;
    
    # 원본 클라이언트의 IP를 전달
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
  • 이렇게 하면, 백엔드 서버는 request.getHeader("X-Real-IP") 또는 request.getHeader("X-Forwarded-For") 안에 실제 클라이언트 IP가 들어있는 걸 확인할 수 있게 됩니다.
  • 다만 기본적으로 request.getRemoteAddr() 값 자체는 여전히 프록시(Nginx) IP로 남습니다.
  • Java/Tomcat/스프링에서는 X-Forwarded-For를 자동으로 해석해 주는 RemoteIpValve(Tomcat)나 스프링의 ForwardedHeaderFilter 등을 설정해야, request.getRemoteAddr()가 원본 IP로 바뀌도록 지원할 수도 있습니다.

3. 요약

  1. proxy_set_header Host $host; 설정의 유무는
    • 백엔드가 어떤 Host 헤더를 받느냐에만 영향을 준다.
    • 즉, 백엔드에서 request.getHeader("Host") 값을 확인했을 때 달라진다.
    • Host: exam.com으로 갈지, Host: api-server로 갈지가 달라지는 것.
  2. request.getRemoteAddr()(서버 단에서 직접 보는 IP)는
    • 설정과 무관하게 “Nginx(프록시)와 연결된 TCP 소켓의 IP” 이므로 기본적으로 Nginx IP가 나온다.
    • 원본 클라이언트 IP를 보고 싶으면, X-Real-IP, X-Forwarded-For를 세팅하고, 백엔드(서버) 측에서 이를 해석하는 설정을 추가로 해야 한다.

결론적으로,

  • proxy_set_header Host $host; 설정을 추가했다/안 했다”에 따라 Host 헤더는 달라질 수 있지만,
  • request.getRemoteAddr()는 (추가적인 IP 전달 설정을 하지 않는 한) 변함없이 Nginx 프록시 IP를 반환한다는 점을 기억하시면 됩니다.

보안 헤더 설정

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

보안 헤더 상세 설명

1. X-Frame-Options

add_header X-Frame-Options "SAMEORIGIN" always;
  • 클릭재킹 공격 방지
  • 프레임 내 페이지 로드를 같은 출처로만 제한
  • 옵션:
    • DENY: 모든 프레임 내 로드 차단
    • SAMEORIGIN: 같은 출처만 허용
    • ALLOW-FROM uri: 특정 출처만 허용

2. X-XSS-Protection

add_header X-XSS-Protection "1; mode=block" always;
  • 크로스 사이트 스크립팅(XSS) 공격 방지
  • 브라우저의 내장 XSS 필터 활성화
  • 옵션:
    • 0: 필터 비활성화
    • 1: 필터 활성화
    • 1; mode=block: 필터 활성화 및 페이지 로드 차단

3. X-Content-Type-Options

add_header X-Content-Type-Options "nosniff" always;
  • MIME 타입 스니핑 방지
  • 선언된 콘텐츠 타입으로만 리소스 해석
  • 브라우저가 콘텐츠 타입을 추측하지 못하도록 함

4. Referrer-Policy

add_header Referrer-Policy "no-referrer-when-downgrade" always;
  • 리퍼러 정보 전송 정책 설정
  • no-referrer-when-downgrade: HTTPS → HTTP로 이동 시 리퍼러 정보 미전송
  • 다른 옵션:
    • no-referrer: 리퍼러 정보 미전송
    • same-origin: 같은 출처만 전송
    • strict-origin: HTTPS → HTTPS만 전송

5. Content-Security-Policy

add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
  • 웹 애플리케이션의 리소스 로드 정책 설정
  • XSS 및 기타 주입 공격 방지
  • 설정값 설명:
    • 'self': 같은 출처의 리소스만 허용
    • http: https:: HTTP/HTTPS 프로토콜 허용
    • data:: data URL 허용
    • blob:: Blob URL 허용
    • 'unsafe-inline': 인라인 스크립트/스타일 허용

공통 속성

모든 보안 헤더에 사용된 always 키워드:

  • 응답 코드와 관계없이 항상 헤더 추가
  • 오류 페이지 등에도 보안 헤더 적용
  • 일관된 보안 정책 유지를 위해 권장

권장 사항

  1. 환경별 설정 분리
    • 개발/스테이징/프로덕션 환경별로 다른 설정 사용
    • 환경변수나 include 파일 활용
  2. SSL/TLS 설정 추가
    • HTTPS 사용 강제
    • 최신 TLS 버전 사용
    • 강력한 암호화 스위트 설정
  3. 로깅 설정 추가
    • 접근 로그
    • 오류 로그
    • 로그 포맷 커스터마이징
  4. 성능 최적화
    • Gzip 압축 활성화
    • 브라우저 캐싱 설정
    • 버퍼 크기 최적화
  5. 주기적인 설정 검토
    • 보안 취약점 점검
    • 최신 보안 권장사항 적용
    • 불필요한 설정 제거

댓글남기기