[React] 프로젝트 배포 후 만난 에러,,,

hee__wed·2024년 9월 6일
0

에러해결

목록 보기
6/7
post-thumbnail

배포하고 처음 접해보는 에러가 많았고, 이전에 겪어본 CORS에러도 fastapi만의 특징이 있어서 같이 정리해봤다.

💥어떤 문제가 있었을까?💥
1. local에서는 잘 되는데 배포환경에서 동작되지 않음
2. 배포서버에서 웹소켓 연동안됨
3. cytoscape로 구현한 네트워크그래프 노드의 이미지가 일부만 보임
4. fastapi CORS 에러




1. local에서는 잘 되는데 배포환경에서 동작되지 않음

배포서버에서 사이트에 들어가보니 login부터 에러가 발생하였다 (그래,,,좋았쒀,,)

docker로 돌렸을때는 잘 동작이 되었기에 프론트의 코드에는 문제가 없을거라 생각하였고, CORS에러도 아니였다. network탭에서 Header와 Response부분을 보고 있었는데 처음 보는 경고문을 접하게 되었다. 저 작은 글씨가...보이시나요...?

This attempt to set a cookie via a Set-Cookie header was blocked because it has ”Secure" attribute but was not received over a secure connection.

해당 에러에 대해 찾아보니 Cookie의 Secure 속성에 문제가 있는것이고, 해당 부분이 쿠키 세팅을 막는다는 내용이였다. 실제로 로그인을 하면 Cookie가 저장은 되지만 새로고침을 하면 다시 사라지는 것을 개발자도구에서 확인할 수 있었다.


백엔드 코드를 찾아보니, httponly=True, secure=True라고 지정이 되어있다.

현재 우리의 배포서버는 http로 접근을 하고 있는데, secure은 https로 접근을 할 경우 보안기능을 해주는 역할을 하는 것 같다. 한마디로 “https로 접근하는것도 아닌데 왜 secure를 True로 해놓았니?” 라고 말해주고 있는 것이다.
secure부분을 삭제하니 다시 Cookie가 사라지지않고 잘 저장되었다. fastapi라서 그런게 아니라 단순히 http/https 둘 중 어떤 것으로 접근하는지에 대한 차이였다.




2. 배포서버에서 웹소켓 연동안됨

일단 처음에는 ws연동 주소를 local로 해놔서 배포서버로 변경했다.
이제 요청 url은 변경되었지만, 아직도 websocket이 열리지도 않고 바로 닫히는 에러가 발생하였다.

요청 URL를 보면 이제는 local이 아닌 ws연동 주소로 바뀐 것이 확인 가능하고, 콘솔을 봐도 에러의 이유를 알 수 없었기에 서버로그를 보았다.

INFO:     172.28.0.4:55916 - "GET /ws/4/7 HTTP/1.0" 404 Not Found
2024-07-26 13:48:13,480 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2024-07-26 13:48:13,481 INFO sqlalchemy.engine.Engine SELECT users.id, users.email, users.password, users.name, users.age, users.gender, users.job, users.category, users.company, users.region, users.image_url, users.status, users.credit, users.created_at, users.updated_at 
FROM users 
WHERE users.id = $1::BIGINT
2024-07-26 13:48:13,482 INFO sqlalchemy.engine.Engine [cached since 85.65s ago] (10,)
2024-07-26 13:48:13,484 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2024-07-26 13:48:13,485 INFO sqlalchemy.engine.Engine SELECT messages.id, messages.room_id, messages.sender_id, messages.content, messages.timestamp 
FROM messages 
WHERE messages.room_id = $1::INTEGER
2024-07-26 13:48:13,486 INFO sqlalchemy.engine.Engine [cached since 83.09s ago] (4,)
2024-07-26 13:48:13,488 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.email AS users_email, users.password AS users_password, users.name AS users_name, users.age AS users_age, users.gender AS users_gender, users.job AS users_job, users.category AS users_category, users.company AS users_company, users.region AS users_region, users.image_url AS users_image_url, users.status AS users_status, users.credit AS users_credit, users.created_at AS users_created_at, users.updated_at AS users_updated_at 
FROM users 
WHERE users.id IN ($1::BIGINT, $2::BIGINT, $3::BIGINT, $4::BIGINT)
2024-07-26 13:48:13,489 INFO sqlalchemy.engine.Engine [cached since 84.66s ago] (4, 8, 10, 11)
2024-07-26 13:48:13,492 INFO sqlalchemy.engine.Engine ROLLBACK
2024-07-26 13:48:13,493 INFO sqlalchemy.engine.Engine ROLLBACK

연결을 했다가 끊기는게 아니라, 경로 자체를 찾을 수 없다는 에러였다. 이런 내용으로 구글링을 해보았는데 크게 도움이 되지않아, 로그를 복사해 GPT에게 물어보았다.

문제 발생 이유

  1. 엔드포인트 문제
  2. url 오타
  3. 배포환경에서 websocket 지원 안함
  4. 💥ngnix 프록시 서버 및 방화벽 설정 확인💥 → 이게 문제였다!
  5. cors 에러
events {
    worker_connections 1024;
}

http {
    # 백엔드 upstream 설정
    upstream backend {
        server backend:8000;
    }

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        listen 80;

        # frontend
        location / {
            root /var/www/frontend;
            index index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

        # backend
        location /api/ {
            proxy_pass  http://backend;
        }
	
	// 이부분을 추가
	location /ws/ { //웹소캣을 연결을 위한 endpoint
	    proxy_http_version 1.1;
	    proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
    	    proxy_pass  http://backend; //websocket server가 있는 origin
    	}

        location /metrics {
            stub_status;
            allow all;  # 모든 IP 허용 (보안상 특정 IP로 제한할 수 있음)
        }
    }

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;
}

엔직엔스 proxy부분에 ws에 대한 코드가 없어서 http로부터 ws에 연결에 실패한 것으로 보인다. 그래서 proxy server가 클라이언트로부터 upgrade Request를 가로챌 때, 백엔드 서버에서 자체 업그레이드 요청을 명시해주면서 연결상태를 유지하도록 허용해야한다.
자세한 내용 참고




3. 그래프 노드에 이미지가 일부만 보임

cytoscape.js로 구현한 네트워크그래프 노드에 사용자별로 설정한 프로필사진이 보여야 하는데 처음에는 CORS에러로 아무것도 보이지 않았다.
백엔드 쪽에서 수정후 CORS에러는 사라졌지만, 또 다시 4장의 사진만 보이고 다른 사진은 GET 403 에러가 발생했다.
image

💥에러 내용
GET https://m-team-bucket.s3.ap-northeast-2.amazonaws.com/DALL·E 403 (Forbidden)

프론트 코드의 오류라고 생각하여 local로 돌려봤지만, local에서는 에러 없이 잘 출력되었다. 유저의 정보에서 image_url를 localstorage 에 저장하여 가져오는거라 별 다른 조건을 걸지 않아서 더욱 에러의 이유를 찾기 어려웠다....

정말 혹시 몰라서 사진이 나오는것과 안나오는 것들의 주소를 확인해보니 띄어쓰기 유뮤를 파악했고, 다른 사진들의 주소를 수정한 뒤 s3에 다시 저장하니 프로필 사진이 다 나왔다. 에러의 원인은 도메인 규칙 때문이였다.

💥 제5조(등록 기준)
① 3단계 kr도메인이름 등록 기준은 다음 각 호와 같다. <개정 2006.3.13> <개정 2011.3.11>

  • 1. 도메인이름은 영문자[A-Z][a-z], 숫자[0-9], 하이픈[-]으로 구성되어야 한다.
    1. 도메인이름 길이는 2자 이상 63자 이하이어야 한다.
    1. 도메인이름은 하이픈 또는 ‘xn--'로 시작하거나 하이픈으로 끝나지 않아야 한다. <개정 2011.3.11>
      ② 2단계 kr도메인이름 등록 기준은 다음 각호와 같다. <개정 2006.3.13> <개정 2011.3.11>
    1. 도메인이름은 영문자[A-Z][a-z], 한글[유니코드에서 정한 완성된 한글 글자마디], 숫자[0-9], 하이픈[-]의 조합으로 구성되어야 한다. <개정 2019.8.29>

1번을 보면 영문자, 숫자로 이루어져야 하고 특수문자를 -만 가능하다고 명시되어있다. 띄어쓰기와 _가 포함되어있던 url은 도메인 규칙에 적합하지 않아 프로필 사진이 나오지 않았던 것이다. 웹은 정말 신경써야 하는게 많다......!!!



4. fastapi CORS 에러

와우.. 이번 프로젝트하면서 CORS에러는 한 15번정도본 거 같다. 이전 프로젝트를 할 때 한번 경험 해본 적이 있어서 에러가 나는 이유에 대해서는 잘 알고있어 어느부분을 고쳐야하는지 금방 알아챌 수 있었다. 그러나 fastapi는 하나 더 신경써야하는 부분이 있다.

기본적으로 CORS에러는 도메인이 달라서 나는 에러이기 때문에 orign=[”*”]으로 지정해놓으면 해결이 되지만 구글링 해보니 fastapi는 origin을 모든 도메인으로 설정할 경우, 보안측면에서 allow_credentials=True 와 충돌이 나서 해결할 수 없다는 글이 많았다.

  origins = ["http://localhost:5173"] //"*"로 하면
  
  app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True, //이부분과 충돌이 남!
    allow_methods=["*"],
    allow_headers=["*"],
)

그래서 origin을 모든 도메인으로 설정하지 않고 해당 코드처럼 도메인을 지정해주었다.

profile
키보드 뚱땅뚱땅 거리기~

0개의 댓글