PLAYDATA 주간회고

플레이데이터 풀스택 백엔드 9기 6월 2주차 회고

Berry-mas 2025. 6. 17. 01:11

플레이데이터 풀스택 백엔드 9기 13주차 주간회고 및 학습기록 (열세번째 기록)


이번 주에는 3차 프로젝트로 영화 추천/리뷰 사이트 백엔드를 구현했다. 이번 프로젝트는 MSA(Microservices Architecture) 기반으로 구성되어 각 서비스 간 연동과 설정이 중요했다. 그러나 예상보다 MSA 설정 및 API 연동이 복잡하고 까다로워서 프로젝트를 마무리하는 데 시간이 오래 걸렸다. 프로젝트를 진행하면서 가장 어려웠던 부분을 중심으로 과정을 정리하고자 한다.

 

1. Spring Cloud Config와 GitHub 연동: msa-config.yml 관리 전략

JWT 토큰의 서명을 위한 비밀키가 담긴 msa-config.yml 파일을 github의 private repository에서 관리하기로 하였다. 

config-service의 application.yml에서 공개키를 이용하여 SSH를 가져와 private repository에 접근할 수 있도록 하였다.

 

이를 위해서 우선 SSH key를 생성해야했다. cmd창을 열어 SSH 공개/비공개 키 쌍을 생성하는 다음의 명령을 입력하였다.

ssh-keygen -m PEM -t rsa -b 4096 -C "git account"

- m pem : PEM 형식으로 키 저장 (Config Server 등에서 YAML 파일에 붙이기 좋음)

- t rsa : RSA 알고리즘 사용

- b 4096 : 키 길이 4096 비트

 

이를 입력하면 cmd 창에 두 경로가 나온다. (스크린샷으로 남겨놓는 걸 잊어버렸다ㅜ)

C:\Users\xxx\.ssh/id_rsa.
C:\Users\xxx\.ssh/id_rsa.pub.

각 경로에 들어가면 엄청난 양의 글자가 들어있다.

id_rsa.pub에는 공개키가, id_rsa 파일에는 비공개키(개인키)가 들어있다.

id.rsa 파일의 시작 부분
id_rsa.pub 파일의 끝 부분

 

id_rsa.pub의 내용을 전부 복사해서 (맨 마지막에 space가 들어가지 않도로 조심한다) github private repository > setting > Deploy keys에 등록한다.

 

이후 id_rsa의 내용을 전부 복사해서 application-sercret.yml 파일에 다음과 같이 작성한다.

application-sercret.yml의 시작 부분
application-sercret.yml의 끝 부분

 

application.yml 파일에는 다음과 같이 작성해서 application-secret.yml 파일을 연결한다.

 

🚩.gitignore에 application-secret.yml을 추가하여 github에 올라가지 않도록 한다. 물론 add 및 commit 후 application-secret.yml을 추가하여 git의 추적을 받지 않도록 해야 한다.

(비밀키는 절대 public github repository에 올라가면 안된다고 한다. 사실 이렇게 하드코딩하는 것도 보안에 있어서 좋지 않다고 하는데, 일단 운영할 사이트를 목적으로 config-service를 구현한 것은 아니기 때문에 이와 같은 방식을 채택했다.)

src/main/resources/application-secret.yml

 

🚩 운영용 시스템에서는 비밀키를 application-secret.yml 같은 설정 파일에 직접 하드코딩하는 방식은 절대 사용하면 안된다고 한다. 환경변수(Environment Variable)를 사용하는 방식이나 Vault, AWS Secrets Manager, Azure Key Vault 등 비밀관리 도구를 사용하는 방식 등은 나중에 해볼 예정이다.


2. 내 FeignClient가 계속 실패한 이유 : 서비스 간 호출 실패 원인 

FeignClient

Netflix에서 개발한 Http Client로, 
REST API 호출 코드를 인터페이스 한 장으로 끝내게 해주는 선언형(Declarative) HTTP 클라이언트
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

'spring-cloud-starter-openfeign' 의존성을 추가하고 서비스 간 통신용 인터페이스에 @FeignClient 어노테이션을 붙이면 REST API를 끌어쓸 수 있다. (Application 클래스에 @EnableFeignClients를 추가하고, @FeignClient에 올바른 서비스 이름 or URL을 지정하는 것을 잊으면 안된다)

 

이번 프로젝트에서의 사용 예시

review-service의 일부
review-service의 api를 호출하는 recommend-service의 ReviewClient 클래스

 

포인트 주의할 점
@FeignClient(name = "review-service") Eureka 등록된 서비스 이름과 반드시 일치해야 함. 틀리면 404 or RetryableException 발생
@GetMapping("/stars/age-group/{ageGroup}/gender/{gender}") 경로가 컨트롤러의 실제 매핑과 정확히 일치해야함. 하나라도 어긋나면 Feign은 404
반환 타입이 List<StarRatingDto> Feign은 내부적으로 ObjectMapper로 역직렬화 → DTO 필드명이 다르면 null 반환 또는 예외
@PathVariable @PathVariable 이름이 맞아야  (@PathVariable("gender") 이런 식으로 명시하면 더 안전)
reviewRepository.findAll().stream() 리뷰를 직접 조회하고, User 정보를 userClient로 다시 불러오는 구조 → 다중 API 호출 문제 (N+1 유사)
(많은 양의 리뷰를 관리, 운영하는 실제 웹사이트를 구현할 때는 이런 방식이 성능에 큰 과부하를 줄 수 있음)

 


팀원 각자 기능을 구현할 때까지만 해도 순탄하게 진행되는 것처럼 보였다.
하지만 막상 마이크로서비스 간의 API를 연결하고 데이터를 주고받기 시작하자, 예상하지 못한 오류들이 연이어 발생했다.
특히 DTO 필드명 불일치, 경로 오탈자, FeignClient 응답 파싱 실패 등은 테스트 단계에서 큰 시간 낭비로 이어졌다.

(당연히 있을 거라고 생각했던 api가 없어서 서로 당황했던 일도 꽤 많았다. 사전 작업이 이렇게 중요하다)

4차 프로젝트에서는 이러한 문제를 줄이기 위해 사전에 API 명세서, 필드명, 변수 타입, 테이블 컬럼명까지 모두 통일하고 검토하는 시간을 반드시 확보할 계획이다.