본문 바로가기
Backend

[Backend] API 흐름 분석: 인증부터 폴백까지의 전 과정 추적

by coding_whale 2026. 4. 27.
반응형

현대적인 웹/앱 서비스에서 API 요청 한 건이 통과하는 경로는 생각보다 복잡하다. 단순히 데이터베이스를 조회하는 것을 넘어, "이 사용자가 누구인지" 검증하고, "어떤 데이터를 보여줄지" 비즈니스적으로 판단하며, 만약 결과가 부족할 경우 "어떤 대안(Fallback)을 보여줄지"까지 결정해야 하기 때문이다.

오늘은 일반적인 API 요청이 시스템을 통과하는 전체 경로를 계층별 코드 포인트와 함께 따라가며, 효율적인 백엔드 설계의 원리를 정리해 본다.

 

1. 전체 흐름 시각화 

시스템의 전체적인 구조를 이해하는 것이 우선이다. 요청은 클라이언트에서 시작하여 보안 필터를 거쳐 비즈니스 로직의 핵심인 서비스 계층으로 도달한다.

 

2. 계층별 세부 추적 및 설계 원칙

🔍 STEP 1. Security Filter: 인증과 보안 컨텍스트

가장 먼저 보안 필터(예: JwtAuthenticationFilter)가 동작한다. 이곳은 **"어디서 사용자가 인증되는가?"**에 대한 답을 주는 지점이다.

  • 핵심 역할: HTTP 헤더의 Authorization 토큰을 읽어 유효성을 검사하고, 성공 시 Spring Security의 SecurityContext에 사용자 정보를 저장한다.
  • 필터 처리의 장점: 인증 정보를 컨트롤러가 아닌 필터에서 미리 처리하면, 비즈니스 로직이 보안 검증 로직과 섞이지 않아 코드가 깔끔해진다. 또한, 잘못된 요청을 시스템 입구에서 빠르게 걸러낼 수 있어 불필요한 서버 자원 낭비를 막을 수 있다.

 

🔍 STEP 2. Controller: 요청 수신과 위임

인증을 통과한 요청은 API 엔드포인트를 담당하는 Controller에 도착한다.

@GetMapping("/items")
public ApiResponse<List<ItemResponseDto>> getItems(@CurrentUser User user) {
    // 1. 서비스 레이어에 비즈니스 로직 처리 위임
    List<Item> items = itemService.getItemsForUser(user.getId());
    
    // 2. 도메인 엔티티를 클라이언트 규격(DTO)으로 변환하여 반환
    return ApiResponse.ok(items.stream().map(ItemResponseDto::from).toList());
}
  • 위임의 이유: 컨트롤러가 서비스에 로직을 위임하는 근본적인 이유는 **'재사용성'**과 '테스트 용이성' 때문이다. 웹, 모바일, 혹은 관리자 페이지 등 다양한 경로로 들어오는 요청에 대해 동일한 비즈니스 규칙을 적용할 수 있고, HTTP 요청 없이도 핵심 로직만 따로 떼어 테스트하기 쉬워진다.

 

🔍 STEP 3. Service: 비즈니스 로직과 결정

Service 계층은 시스템의 '두뇌' 역할을 하며, 사용자 상태에 따라 어떤 데이터를 어떻게 조회할지 결정한다.

@Service
@RequiredArgsConstructor
public class ItemService {

    private final ItemRepository itemRepository;
    private final PreferenceRepository preferenceRepository;

    @Transactional(readOnly = true)
    public List<Item> getItemsForUser(Long userId) {
        // 1. 사용자의 활동 기록 및 선호도 메타데이터 확보 (없을 시 기본값 생성)
        UserPreference preference = preferenceRepository.findByUserId(userId)
                .orElseGet(() -> createDefaultPreference(userId));

        // 2. 사용자 취향 기반으로 현재 상황에 최적화된 랭킹/필터 전략 수립
        SearchStrategy strategy = preference.calculateOptimalStrategy();
        
        // 3. 리포지토리를 통한 다단계 조회 실행
        // (내부적으로 1순위 조회 -> 부족 시 2순위 -> ... 순으로 동작)
        return itemRepository.findItemsByStrategy(strategy);
    }
}
  • 동작 방식:저장된 사용자 기록이 없다면 초기값을 생성하는 등 예외 상황을 처리한다. 어떤 데이터를 먼저 보여줄지, 어떤 필터를 적용할지 등의 모든 '비즈니스적 결정'이 여기서 일어나며 리포지토리에 조회 재료(Strategy)를 전달한다.

 

🔍 STEP 4. Repository: 데이터 조회 및 폴백(Fallback) 전략

최종적으로 데이터를 가져오는 단계다. 실무에서는 조건에 맞는 데이터가 없을 때를 대비한 '다단계 조회'가 필수적이다.

@Repository
@RequiredArgsConstructor
public class ItemRepositoryImpl implements ItemRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    @Override
    public List<Item> findItemsWithFallback(SearchStrategy strategy) {
        // 1단계: 초개인화 매칭 (모든 필터 적용)
        List<Item> results = findPersonalizedItems(strategy);
        
        // 2단계: 결과가 부족할 경우 관심 카테고리로 확장
        if (results.size() < 10) {
            results.addAll(findCategoryItems(strategy, results));
        }
        
        // 3단계: 여전히 부족하면 실시간 인기 데이터 추가
        if (results.size() < 10) {
            results.addAll(findTrendingItems(results));
        }
        
        // 4단계: 최종적으로 시스템의 활성 데이터를 무작위로 채워 응답 보장
        if (results.size() < 10) {
            results.addAll(findRandomActiveItems(results));
        }
        
        return results.stream().distinct().limit(10).toList();
    }
}
1순위 사용자 맞춤형 큐레이션 가장 정교한 개인화 데이터
2순위 선호 카테고리 기반 조회 관심사 위주의 일반 데이터
3순위 실시간 인기/트렌드 항목 대중적인 인기 데이터 확보
최종 기본 리스트 전체 조회 결과가 비어있지 않도록 보장
  • 폴백 설계의 핵심: 하위 단계로 내려갈수록 조회 쿼리의 조건은 단순해져야 한다. 예를 들어 1순위는 지역=A AND 관심사=B AND 연령대=C처럼 까다롭지만, 최종 단계는 단순히 STATUS=ACTIVE와 같은 최소한의 상태 조건만 남긴다.
  • 폴백의 중요성: 만약 이런 폴백 로직이 없다면, 취향 데이터가 적은 신규 사용자나 조건이 너무 까다로운 사용자에게 "결과가 없습니다"라는 빈 화면을 보여주게 된다. 이는 서비스 이탈의 결정적인 원인이 된다. 따라서 어떠한 상황에서도 유효한 데이터를 반환하도록 설계하는 것이 사용자 경험(UX)의 핵심이다.

 

3. 실전 트러블슈팅 및 개선 아이디어

💡 관찰 포인트: 시스템 안정성 체크

  1. 인증 실패 시: 토큰 누락이나 만료 시 필터에서 즉시 차단되어 401 에러가 발생한다.
  2. 신규 사용자 유입: 개인화 데이터가 없더라도 서비스 레이어의 기본값 생성 로직과 리포지토리의 하위 순위 폴백(인기 항목 등)이 작동하여 정상적인 화면을 보여준다.
  3. 부하 분산: 복잡한 1순위 조회가 느려진다면, 일시적으로 낮은 단계의 폴백 결과를 바로 반환하는 서킷 브레이커 패턴을 적용하여 시스템 전체의 가용성을 높일 수 있다.

🚀 운영 가시성 확보를 위한 팁

  • Context Logging: 로그에 사용자의 ID뿐만 아니라 현재 어떤 단계의 폴백(1순위인지, 최종 단계인지)을 탔는지 기록해 보자. 이는 서비스 품질을 분석하고 추천 알고리즘을 개선하는 데 소중한 데이터가 된다.
  • Cache Layer 활용: 3순위나 최종 단계의 일반적인 데이터는 Redis 같은 캐시에 저장해두면 DB 부하를 획기적으로 줄이면서도 빠른 응답 속도를 유지할 수 있다.

 

반응형