Growth Hoon

[원티드 포텐업 BE 1기] LXP 프로젝트 4차 후기 본문

원티드 포텐업 백엔드 1기

[원티드 포텐업 BE 1기] LXP 프로젝트 4차 후기

sayhoon 2026. 1. 23. 00:38

4차 프로젝트 시작 전 : 리팩토링 주간

프로젝트가 시작되기 전에 백엔드 팀에서 리팩토링 기간을 가지기로 했다.

이유는 서버의 CPU랑 네트워크가 튀는 구간이 있었기 때문이고, 내부적으로 코드 품질도 떨어졌기 때문이다.

 

리팩토링 주간에서 내가 담당했던 유저, 인증, 인가 부분도 변경하게 되었다. 

기존에 access token, refresh token을 프론트에서만 관리를 하고 있어 refresh token이 탈취 당했을 때, 보안적으로 문제가 되어 리팩토링하기로 했다.

최종적으로 백엔드 내부적으로 RTR(Refresh Token Rotate)을 적용하여 refresh token이 탈취되어도 DB에 저장한 토큰값이랑 비교하여 탈취된 토큰을 방지할 수 있다.

> 참조한 블로그 사이트

 

4차 프로젝트의 MVP

리팩토링 주간이 끝나고 4차 프로젝트 기간에 구현할 최소 기능 구현을 프론트팀과 논의를 진행하였다.

Admin 페이지, 비디오 업로드 및 재생, 배포를 메인으로 MVP를 구성하였다.

 

하지만 문제는 백엔드 개발자가 총 5명이었고, 최소 기능을 구현하는데 인원이 너무 많았었다.

그래서 각 팀원들에게 이번 프로젝트에서 하고 싶은 것이 따로 있는지 수요 조사를 했고,

이번 챕터에서 배운 백엔드 모니터링 및 CI/CD를 도입하기로 했다.

 

최종적으로 백엔드 팀에서 Admin API, Video upload & Streaming, CI/CD, Monitoring, Lecture Refactoring으로 구분되어 개발이 진행이 되었다.

 

내가 담당한 파트는 Admin API 파트이다.

위 사진 처럼 총 4개의 API가 만들어졌고, 일반 API 서버랑 Admin API 서버가 분리되지 않았고

Admin이 사용할 수 있는 API (강의 승인 및 반려 처리)들에 대해서 MEMBER유저가 사용할 수 없도록 User_Role의 관리가 필요했다. 

 

그래서 Security Config에서 API마다 권한 설정을 하여 admin만 사용할 수 있도록 해주었다.

 

이슈 및 해결 방법 

1. Http 및 도메인 적용 과정

처음에 프론트 서버를 GCP에 배포를 했을 때, Http로 배포를 했는데, 클라이언트 측에 쿠키를 셋팅하는 셋팅 값이 Secure=True로 설정을 해서 로그인을 한 후 다른 페이지로 넘어갈 때, 쿠키가 사라져서 로그인이 풀리는 문제가 발생했다.

 

Secure=True라는 플래그는 "이 쿠키는 반드시 HTTPS를 통해서만 전송되어야 한다"이기 때문에 HTTP서버의 응답 헤더로 Set-Cookie를 클라이언트(브라우저)가 받을 경우 보안 위협으로 간주하게 되어 저장을 거부하거나 쿠키를 삭제한다.

 

이 문제를 해결하는 방법을 찾아봤을 때, Secure=False로 변경을 하거나 HTTPS를 도입하는 방법이 있었는데 팀 내에서는 실서비스를 기준으로 하여 HTTPS도 도입을 해보자는 의견이 모아져서 HTTPS를 도입하게 되었다.

> Secure=False로 설정하여 HTTP로 서비스를 운영하게 되면 통신 구간이 암호화 되지 않아서 패킷 탈취 시, access token이나 refresh token이 그대로 노출될 수 있어서 보안적으로 위험하기 때문에 HTTPS를 도입하게 되었다.

 

실제로 가비아를 통해 도메인을 구입하였고, 구입한 도메인을 기반으로 HTTPS를 적용하여 클라이언트(브라우저)에서 쿠키가 사라지는 문제를 해결할 수 있게 되었다.

> 도메인을 구입해야 했던 이유는 HTTPS는 서버의 IP가 아닌 도메인을 기준으로 신뢰를 보장하기 때문이다.

> HTTPS는 SSL/TLS 인증서 기반 통신으로 인증서 내부에 도메인 주소가 포함되어있다. 

> HTTPS 접속 시, 브라우저가 아래의 순서대로 검증을 진행한다.

1. 서버가 인증서를 전달

2. 인증서 안의 도메인 확인

3. URL의 도메인과 일치하는지 검사

4. 신뢰 가능한 CA인지 확인 

> 그렇기 때문에 도메인을 구입해야 HTTPS를 적용할 수 있다 !

 

 

이후 API 호출이 되지 않는 문제가 있었는데 이는 API 서버의 CORS 설정에서 localhost와 IP 주소만 허용하고 구입한 도메인 주소로 Origin을 설정하기 않아 문제가 발생했다.

 

최종적으로 사진 처럼 CORS를 설정하여 API가 호출 되는 것을 확인할 수 있었다.

 

2. 비디오 재생 및 업로드 설계 과정

초기 설계에서는 그림과 같이 비디오 업로드를 백엔드 서버에서 책임지는 구조를 고려했다.
클라이언트는 비디오 업로드 API를 호출하고, 백엔드 서버는 해당 영상을 GCS에 업로드한 뒤 결과를 관리하는 방식이다.

 

업로드 중 실패가 발생할 경우를 대비해 최대 3회까지 retry를 수행하고, 그럼에도 실패할 경우 업로드 실패 메시지를 클라이언트에 전달하려는 목적이었다. 이 과정에서 “업로드 결과를 클라이언트에 어떻게 전달할 것인가”에 대한 논의가 있었고, SSE(Server-Sent Events) 를 이용해 업로드 완료 또는 실패 상태를 전달하는 방식을 고려했다.

 

여기서 비디오 업로드 API를 동기적으로 처리할 경우 업로드가 완료될 때까지 요청이 블로킹되어 다른 API 요청에 영향을 줄 수 있다고 판단했기 때문에, 해당 API만 비동기로 처리하고 처리 완료 시점에 SSE로 결과를 전달하는 구조를 설계했다.

 

하지만 이 과정에서 몇 가지 문제가 드러났다.

  1. "백엔드 서버가 대용량 영상 파일을 직접 처리할 경우, API 서버의 부하가 크게 증가한다."
    • 비디오 파일의 최대 크기는 1GB로 정책상 설정을 하였는데, 이러한 대용량 파일을 백엔드 서버가 직접 수신을 하게 되면
      API 서버의 메모리, 네트워크, 스레드 자원을 크게 소모하게 된다.
    • 즉, API 서버의 안정성과 확장성에 직접적인 영향을 주는 구조인 셈이다.
  2. "프론트엔드 입장에서 업로드 진행률(progress) 을 확인하기 어렵다."
    • 해당 구조에서는 프론트엔드가 백엔드로 파일을 한번에 전송하고 실제 업로드는 백엔드와 GCS에서 발생한다.
    • 이 때문에, 브라우저는 현재 몇 %가 업로드 되었는지, 네트워크로 멈춘 상태인지 확인할 수 없다.
    • 결과 이벤트는 전달할 수 있지만, 실시간 진행률 제공에는 적합하지 않았던 구조이다.
  3. "SSE(Server-Sent Events)를 도입하는 데 필요한 구현 난이도와 운영 리스크를 충분히 고려하지 못했다."
    • SSE는 서버에서 클라이언트 단방향 통신에 적합한 기술이지만, 대용량 업로드 진행률을 지속적으로 전달하기 위해 사용하기에는 구현 복잡도와 운영 리스크가 크다고 한다.
    • 결과적으로 서버 부담은 커지고, UX도 개선되기 어려운 구조이다.

최종적으로 다음과 같은 흐름으로 구조를 변경했다.

  1. 클라이언트가 비디오 업로드를 요청한다.
  2. 백엔드 서버는 GCS에 업로드할 수 있는 Signed URL을 생성해 클라이언트에 전달한다.
  3. 클라이언트는 해당 Signed URL을 이용해 GCS로 직접 비디오를 업로드한다.
  4. 업로드 진행률은 브라우저에서 직접 추적하여 사용자에게 실시간으로 표시한다.
  5. 업로드가 완료되면 클라이언트가 Upload Complete API를 호출한다.
  6. 백엔드는 업로드 완료 정보를 DB(MySQL)에 저장한다.

 

이렇게 설계를 전환하여 API 서버 부하감소, 업로드 진행률 UX 개선, 책임 분리 명확화를 할 수 있게되었다.

 

또한 GCS는 video MIME 타입 파일에 대해 HTTP Range 요청을 지원하기 때문에, 브라우저에서 비디오를 스트리밍 방식으로 재생할 수 있다.

 

이로 인해 비디오 스트리밍을 위한 별도의 백엔드 로직을 구현하지 않아도 되는 이점이 있었다.

 

후기

이번 프로젝트는 프론트엔드와 백엔드 팀 모두 GCP 환경에서 실제 배포를 경험하고, 도메인을 구매해 HTTPS를 직접 적용해본 프로젝트였다.

 

이전 프로젝트에서는 VM의 CPU, 메모리 상태만 확인하며 리팩토링을 진행했는데, 이번에는 모니터링 시스템을 도입하면서 어떤 API에서 문제가 발생하는지, 어떤 플로우에서 에러가 집중되는지를 실제로 확인할 수 있었다.

 

이를 통해 단순히 “서버가 느리다”가 아니라, 운영 환경에서 이슈를 어떻게 발견하고 원인을 추적하는지에 대한 감을 얻을 수 있었다는 점이 가장 큰 수확이었다.

 

 

아쉬운 점도 있었다.
실제 배포를 진행하면서 서비스를 public 환경으로만 운영하게 되어 Develop 환경과 Production 환경을 명확히 분리하지 못했다.

 

다음 프로젝트에서는 GCP에서 제공하는 IAP(Identity-Aware Proxy) 를 활용해 외부에서는 접근할 수 없지만, 실제 운영 환경과 유사한 Develop 환경을 구성해보고 싶다.

 

이를 위해서는 팀 전반적으로 Mock Server에 대한 이해와 테스트 레벨(Unit Test, Integration Test, E2E Test) 에 대한 공감대가 먼저 필요하다고 느꼈다.

 

다음 프로젝트에서는 단순한 기능 구현을 넘어, 개발·테스트·배포 환경이 분리된 구조를 팀 차원에서 설계해보는 것을 목표로 삼고자 한다.