서론
07/18 ~ 09/10 : 기업 참여 프로젝트(파이널 프로젝트)
08/25 : 백엔드-프런트 병합
08/26 : 백엔드-프런트 병합
08/27 : 산출물 제작
08/28 : 휴가 / IR 발표 준비
08/29 : 휴가 / IR 발표
테스트 케이스와 테스트 결과서 등의 산출물 초안을 AI 에이전트에게 맡겨 보았습니다. 기존에 수기로 작성했다면 며칠 걸릴 일도 몇 시간만에 해냈습니다. AI 에이전트의 힘을 크게 느꼈습니다.
1. 파이널 프로젝트
테스트 케이스, 테스트 결과서 등을 만들었습니다.
드디어 병합이 마무리가 되었고, 요약봇이 정상작동 하는 것을 확인했습니다.
아래 DB에도 잘 저장되고 요금 및 일관성을 고려해 재생성을 막고 DB에서 불러오도록 했습니다.
또, 각 뉴스마다 카테고리별로 뉴스를 받아서 해당하는 프롬프트를 다르게 반영해주는 기능을 구사한 것입니다. 이제 추가적으로 프롬프트를 정규화하는 것에 초점을 맞출 예정입니다.
팀원이 뉴스레터 전송 기능을 만들고 있습니다. 거기에 들어갈 프롬프트 형식도 짜보고, 적용할 수 있도록 API도 맞춰볼 생각입니다.

팀원이 카카오 API를 가져와 이메일 로그인/재설정을 더불어 카카오 로그인 기능까지 만들었습니다.
스웨거 @Operation 어노테이션에 적힌 대로 소셜 로그인 신규 가입자가 정보를 제출하고 최종 토근을 발급 받는 형식입니다.
흐름은
(프런트) 카카오 로그인 버튼 클릭
-> 카카오 SDK 통해 인가 코드 획득
(백엔드) 이 코드를 받아 카카오 서버에 요청
-> access_token + 사용자 기본 정보(이메일, 닉네임 등) 획득
-> DB 사용자 조회
- 기존 회원 -> 바로 JWT 발급 후 로그인 완료
- 신규 회원 -> 추가 정보 입력 단계로 이동
설명: Service 단에서 처리 : AuthService
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Override
@Transactional
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// 1. 기본 OAuth2UserService 객체 생성
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
// 2. userRequest를 통해 카카오/구글에서 사용자 정보 로드 준비
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
// 3. 카카오/구글에 따라 적절한 UserInfo 객체 생성
OAuth2UserInfo userInfo;
if (registrationId.equals("google")) {
userInfo = new GoogleUserInfo(oAuth2User.getAttributes());
} else if (registrationId.equals("kakao")) {
userInfo = new KakaoUserInfo(oAuth2User.getAttributes());
} else {
throw new OAuth2AuthenticationException("지원하지 않는 OAuth2 공급자입니다: " + registrationId);
}
// 4. User 엔티티로 저장 또는 업데이트
User user = saveOrUpdate(userInfo);
// 5. DefaultOAuth2User 객체 반환
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(user.getRole().name())),
oAuth2User.getAttributes(),
userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()
);
}
private User saveOrUpdate(OAuth2UserInfo userInfo) {
User user = userRepository.findByEmail(userInfo.getEmail())
.map(entity -> entity.updateSocialInfo(userInfo.getName(), userInfo.getProvider(), userInfo.getProviderId()))
.orElseGet(() -> User.builder()
.email(userInfo.getEmail())
.name(userInfo.getName())
.password(passwordEncoder.encode(UUID.randomUUID().toString()))
.role(UserRole.USER)
.provider(userInfo.getProvider())
.providerId(userInfo.getProviderId())
.build());
return userRepository.save(user);
}
}
추가 정보 입력 코드 : AuthController
설명: 추가 정보 입력 단계로 Controller 부분에 코드를 작성
@Operation(
summary = "소셜 로그인 추가 정보 입력",
description = "소셜 로그인 신규 가입자가 추가 정보를 제출하고 최종 토큰을 발급받습니다.",
operationId = "provideAdditionalInfo"
)
@PostMapping("/additional-info")
public ResponseEntity<ApiResult<LoginResponseDto>> provideAdditionalInfo(
@RequestHeader("Authorization") String authorizationHeader,
@Valid @RequestBody AdditionalInfoRequestDto requestDto) {
LoginResponseDto loginResponse = authService.provideAdditionalInfo(authorizationHeader, requestDto);
return ResponseEntity.ok(ApiResult.success(loginResponse));
}
원인
게이트웨이(SSL/JWT) → 뉴스 서비스(Spring) → 요약 API(Flask)로 이어지는 체인에서 요약 호출이 401/402/503 등으로 실패.
여러가지 문제가 있었는데 핵심은 Authorization 헤더 전달, 검증 불일치였습니다.
해결
처음에 Gateway에 JWT 부문을 수정하면서 테스트햇지만, 똑같은 에러를 발산함에 팀원에게 물었더니 2중으로 걸어주는게 보안상 좋다는 이야기를 듣고, NewsService의 JWT도 재점검 해주고, .env.local 오타와 환경변수도 수정해주었습니다.
flaskapi 구동도 해주지 않았던 터라 에러만 수 백번 남발해가며 해맸던 것 같습니다.
다행히 하나하나 해결할 수 있게 log를 찍어 놨던 터라 해결을 할 수 있었습니다.
2. 마무리
코드 리뷰 하며 여러가지 문제점을 확인하고 해결해 나가는 과정을 기록하는 것은 코드의 이해도에 많은 이점을 주는 것 같아 좋았습니다.
자격증도 그렇고 욕심과 자만이 문제인 것 같습니다. 계속해서 하나씩 마무리하며 정리해나가는 과정이 필요한 것 같습니다.
프로젝트: CI/CD, 뉴스레터 + 요약기능 구현, 시연영상 & PPT 제작
이력서, 자소서 작성