인프런 커뮤니티 질문&답변

신유빈님의 프로필 이미지
신유빈

작성한 질문수

Slack 클론 코딩[백엔드 with NestJS + TypeORM]

socket.io 사용 시 cors 에러 with react

해결된 질문

작성

·

2.3K

·

수정됨

0

안녕하세요 제로초님~!

 

최근에 socket.io 문제를 해결해주셔서 감사합니다. 제로초님 덕분에 몇주간 있었던 불면증이 사라졌습니다.

이번에 드리고자 하는 질문은 백엔드 api 서버와 리액트 로컬호스트의 websocket 연결에서 cors 에러가 사라지지 않고 있다는 것입니다.

프론트와 백엔드 둘다 localhost로 사용했을땐 문제 없이 잘 작동하였으나, 백엔드 api 서버와 프론트(리액트) http://localhost:3000 로 연결하려 하니 cors 에러가 사라지질 않네요.

백엔드 api 서버주소는 https://api.yubinhome.com/ 이고, traefik 을 사용해 https 인증을 받고 있습니다.
제가 어떤 부분을 놓친 것인지 함께 봐주실 수 있을까요?

 

에러메시지

 

Access to XMLHttpRequest at 'http://api.yubinhome.com/socket.io/?EIO=4&transport=polling&t=Ohzm2CT' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

 

GET http://api.yubinhome.com/socket.io/?EIO=4&transport=polling&t=OhzmhTk net::ERR_FAILED

 

 

요청헤더

스크린샷 2023-10-05 오전 11.49.31.png

 

응답헤더
스크린샷 2023-10-05 오전 11.49.14.png

  

시도 해 본 방법


1. 공식 문서에 나온대로 api url 에 직접 연결해보니 서버가 작동하고 있는 것은 확실해보입니다.

스크린샷 2023-10-05 오전 11.53.22.png
소켓io 공식문서 cors : https://socket.io/docs/v3/handling-cors/#troubleshooting


2. 서버측 origin, credetials 를 설정하였습니다. origin : "*" 가 문제인가 싶어 http://localhost:3000 url 을 직접 주었습니다.
클라이언트 측에는 withCredentials: true 을 설정해주었습니다.


백엔드 api 서버 코드

https://github.com/fog-of-war/dev-be/blob/dev/src/events/events.gateway.ts

@WebSocketGateway({
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
    allowedHeaders: ["authorization", "Authorization"],
    credentials: true,
  },
  namespace: /\/ws-.+/,
  transports: ["websocket", "polling"],
})




리액트 코드
https://github.com/fog-of-war/dev-fe/blob/46a8de3a13de4039e9aa511a07cfeea23d8a85fa/src/components/Notifications/NoticeNotifications.tsx

REACT_APP_API_URL=https://api.yubinhome.com/

REACT_APP_SOCKET_URL=ws://api.yubinhome.com/v1/ws-alert

let socket: any = null;

socket = io(socketUrl + "-" + userId, {
  withCredentials: true,
  extraHeaders: {
    Authorization: `Bearer ${sanitizedToken}`,
  },
});

 

답변 2

0

신유빈님의 프로필 이미지
신유빈
질문자

함께 찾아봐주셔서 감사합니다 제로초님 👍


아쉽게도 제 역량이 부족해서인지 여러번 다른 방식을 적용해봐도 cors 오류가 계속 지속되어, 프론트 팀원의 로컬에 nest.js + 도커 환경을 설정해주고 개발환경 구성하게끔 진행했습니다ㅎㅎ 최종 배포 시에는 프론트와 백엔드 도메인이 같기에 문제 없는 것 확인하였습니다.

혹시 현업에서는 Localhost 로 개발 시 cors 에러가 일어나면 어떤 방식으로 처리하는지 알려주실수 있을까요?

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

현업에서는 로컬호스트에서 cors가 일어날 일이 없습니다. 로컬호스트는 개발 서버를 바라보고 개발 서버에는 cors 허용이 되어있어서 에러가 발생하지 않습니다.

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

헤더를 보여주실 때 빨간 요청/응답의 헤더를 보여주셔야죠.

Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

이 메시지가 에러의 핵심입니다. Redirect가 일어나서 그렇습니다. 307 internal redirect가 왜 일어나나요?? nginx 같은 거 쓰시나요?

신유빈님의 프로필 이미지
신유빈
질문자

넵넵 트래픽(traefik) 사용합니다!

딱 봐도 아시는군요! 멋지십니다.

빨간색에는 응답헤더가 없는 상태입니다.

docker-compose로 트래픽 설정을 해둔상태입니다.

version: "3.8"
services:
  web:
    image: shinyubin/fow-be
    container_name: fow-be
    restart: always
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "traefik.enable=true"
      - "traefik.http.routers.web.rule=Host(`api.yubinhome.com`)"
      - "traefik.http.routers.web.entrypoints=websecure"
      - "traefik.http.routers.web.tls.certresolver=myresolver"
    ports:
      - "5000:5000"
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
    command: sh -c "npx prisma migrate dev && npm run start:dev"
    environment:
      - DATABASE_URL={DATABASE_URL}
      - JWT_SECRET={JWT_SECRET}
      - GOOGLE_OAUTH_ID={GOOGLE_OAUTH_ID}
      - GOOGLE_OAUTH_SECRET={GOOGLE_OAUTH_SECRET}
      - KAKAO_CLIENT_ID={KAKAO_CLIENT_ID}
      - NAVER_CLIENT_ID={NAVER_CLIENT_ID}
      - NAVER_CLIENT_PW={NAVER_CLIENT_PW}
      - GOOGLE_REDIRECT_URL={GOOGLE_REDIRECT_URL}
      - KAKAO_REDIRECT_URL={KAKAO_REDIRECT_URL}
      - NAVER_REDIRECT_URL={NAVER_REDIRECT_URL}
    networks:
      - freecodecamp
    depends_on:
      - dev-db
  dev-db:
    image: postgres:13
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: 123
      POSTGRES_DB: nest
    networks:
      - freecodecamp
  traefik:
    image: "traefik:v2.0"
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=fogofseoul@gmail.com"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock"
    networks:
      - freecodecamp
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      TZ: Asia/Seoul
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_POLL_INTERVAL: 60
    restart: unless-stopped
networks:
  freecodecamp:

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

https://nhj12311.tistory.com/624

웹서버에서 hsts 관련 옵션 끄면 된다고 합니다. traefik에서도 이 옵션 끌 수 있나 찾아보세요.

신유빈님의 프로필 이미지
신유빈
질문자

  web:
    image: shinyubin/fow-be:latest
    container_name: fow-be
    restart: always
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "traefik.enable=true"
      - "traefik.http.routers.web.rule=Host(`api.yubinhome.com`)"
      - "traefik.http.routers.web.entrypoints=websecure"
      - "traefik.http.routers.web.tls.certresolver=myresolver"
      # HSTS 비활성화
      - "traefik.http.middlewares.hsts.headers.stsPreload=false"


공식문서 sts preload 설정
https://doc.traefik.io/traefik/middlewares/http/headers/#stspreload
hsts 비활성화를 공식문서에서 찾아 추가해보았으나 동일한 에러가 발생하네요.

검색을 통해 이해한 바로는 hsts 를 활성화하면 http 로 오는 요청도 https로 강제로 바꿔주는것으로 보입니다. 그렇다면 ws 프로토콜로 오는 요청도 https 로 강제로 바꿔버릴 수 있어 잘 작동하지 않을수 있기 때문에 제로초님께서 sts preload 옵션을 끄는 것으로 요청하신 것으로 이해가 되네요. 제가 이해한 바가 맞을까요?

추가로 공식문서에 CORS 미들웨어 설정 내용을 찾아 제 docker-compose.yml에 라벨을 추가해보니 oauth 로그인이 불가해진것을 확인하고 주석 처리했습니다.

      # CORS 미들웨어 설정
      # - "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=*"
      # - "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=*"
      # - "traefik.http.middlewares.cors.headers.accesscontrolmaxage=100"
      # - "traefik.http.middlewares.cors.headers.addvaryheader=true"
제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

지금 보시면 http 요청을 보낼 때 https로 강제로 바뀌고 있습니다. 여기서 cors가 발생하는 것이고요. http -> https로 바뀌는 동작을 막아야합니다. 아니면 transport가 현재 polling인데 이걸 websocket으로 설정하시면 http 요청을 처음부터 보내지 않을 겁니다.

신유빈님의 프로필 이미지
신유빈

작성한 질문수

질문하기