개발 프로젝트/[팀 프로젝트] 냉파마스터

장보기 연동과 사전 재료 관리자 기능 정리

namerong 2026. 6. 30. 23:58

이번 작업 범위는 크게 두 가지였다.

첫 번째는 기존 장보기 화면을 백엔드 API와 연결하는 작업이고, 두 번째는 관리자가 사전 재료를 조회, 추가, 수정, 비활성화, 재활성화할 수 있는 API를 정리하는 작업이었다.

진행 이슈

이슈작업 내용
#32 기존 장보기 화면 API 연동
#158 장보기 연동 관련 수정/보완
#33 사전 재료 전체/비활성 목록 조회 API 구현
#34 사전 재료 추가 API 구현
#35 사전 재료 수정 API 구현
#36 사전 재료 비활성화/재활성화 API 구현
#37 장보기 API 명세 오타 및 인증 여부 수정

#32 기존 장보기 화면 API 연동

기존 장보기 화면은 화면 상태 중심으로 동작하고 있었고, 실제 백엔드 데이터와 연결되어 있지 않았다.
이번 작업에서는 기존 UI 구조는 유지하면서 mock state/API 호출 부분을 실제 API 호출로 교체했다.

연동한 주요 흐름은 다음과 같다.

기능 Method URL
장보기 항목 추가 POST /api/v1/shopping-items
장보기 목록 조회 GET /api/v1/shopping-items
장보기 항목 체크/체크 해제 PATCH /api/v1/shopping-items/{shoppingItemId}/check
장보기 항목 삭제 DELETE /api/v1/shopping-items/{shoppingItemId}
장보기 항목 냉장고 반영 POST /api/v1/shopping-items/{shoppingItemId}/fridge

프론트에서는 shoppingApi를 통해 API 호출을 분리하고, 장보기 store에서는 기존 화면에서 사용하던 상태 변경 흐름을 API 응답 기반으로 바꿨다.

특히 장보기 항목을 냉장고에 반영할 때는 백엔드에서 장보기 항목을 기반으로 냉장고 재료를 생성하고, 장보기 목록에서는 해당 항목을 제외하는 흐름으로 처리했다.

#158 장보기 연동 수정/보완

장보기 화면 연동 후 실제 화면에서 확인하면서 API 요청 경로, 인증 토큰 전달, 응답 구조 처리 부분을 보완했다.

프론트와 백엔드는 같은 기능을 바라보더라도 서로 기대하는 데이터 형태가 조금만 달라도 바로 화면 오류로 이어진다.
그래서 단순히 API 호출만 붙이는 것이 아니라, 실제 화면에서 다음 흐름이 자연스럽게 이어지는지 확인했다.

  • 장보기 목록 최초 조회
  • 재료 검색 후 장보기 추가
  • 체크 상태 변경
  • 삭제 후 목록 갱신
  • 냉장고 반영 후 냉장고 화면에서 데이터 확인

이 과정에서 프론트 연동 작업은 “API를 호출한다”보다 “사용자 흐름이 끊기지 않게 상태를 갱신한다”가 더 중요하다는 걸 확인했다.

#33 사전 재료 전체/비활성 목록 조회 API 구현

관리자 화면에서 사전 재료를 관리하려면 활성 재료뿐 아니라 비활성 재료도 확인할 수 있어야 했다.

그래서 관리자 전용 API로 전체 사전 재료 목록과 비활성 사전 재료 목록을 분리했다.

기능 Method URL
사전 재료 전체 목록 조회 GET /api/v1/admin/products
비활성 사전 재료 목록 조회 GET /api/v1/admin/products/inactive

이 API는 일반 사용자가 아니라 관리자만 접근해야 하므로 /api/v1/admin/** 경로의 보안 정책을 그대로 사용했다.

서비스에서는 Product 엔티티를 조회한 뒤 AdminProductResponse로 변환해서 반환했다.
사용자용 사전 재료 검색 API와 달리 관리자 API는 isActive 값도 함께 내려줘야 하기 때문에 별도 응답 DTO를 사용했다.

#34 사전 재료 추가 API 구현

관리자가 새로운 사전 재료를 등록할 수 있는 API를 구현했다.

기능 Method URL
사전 재료 추가 POST /api/v1/admin/products

요청값은 다음 정보를 받는다.

  • productCategoryId
  • name
  • defaultExpiryDays

사전 재료명은 DB에서 unique 제약이 걸려 있다. 하지만 DB 에러가 그대로 터지면 응답이 지저분해지기 때문에, 서비스에서 먼저 existsByName으로 중복 여부를 확인했다.

if (adminProductRepository.existsByName(request.name())) {
    throw new DuplicateProductNameException();
}

중복 재료명은 공통 예외 핸들러에서 409 Conflict로 처리되도록 연결했다.

이 작업을 하면서 Repository에 메서드를 선언하는 이유도 다시 정리했다.
Spring Data JPA는 existsByName 같은 메서드명을 보고 쿼리를 자동 생성한다.
즉, 직접 SQL을 작성하지 않아도 products.name 기준으로 존재 여부를 확인할 수 있다.

#35 사전 재료 수정 API 구현

기존 사전 재료의 카테고리, 이름, 기본 유통기한을 수정하는 API를 구현했다.

기능 Method URL
사전 재료 수정 PATCH /api/v1/admin/products/{productId}

수정 로직에서 중요한 부분은 중복 이름 검증이었다.

같은 재료를 수정하면서 이름을 그대로 보내는 경우는 정상 요청이다.
하지만 다른 재료가 이미 사용 중인 이름으로 변경하려고 하면 막아야 한다.

그래서 아래처럼 처리했다.

if (!product.getName().equals(request.name())
        && adminProductRepository.existsByName(request.name())) {
    throw new DuplicateProductNameException();
}

이 조건은 다음 의미다.

  • 현재 재료의 기존 이름과 요청 이름이 같으면 통과
  • 이름이 변경되는 경우에만 중복 검사
  • 다른 재료가 이미 같은 이름을 쓰고 있으면 예외 발생

엔티티에는 update() 메서드를 두고, 서비스에서는 엔티티의 상태를 변경하도록 했다.

product.update(
        request.productCategoryId(),
        request.name(),
        request.defaultExpiryDays()
);

update() 메서드가 값을 return하지 않는 이유는, JPA가 영속 상태의 엔티티 변경을 감지해서 트랜잭션 종료 시점에 UPDATE 쿼리를 반영하기 때문이다.

#36 사전 재료 비활성화/재활성화 API 구현

사전 재료는 실제 삭제가 아니라 isActive 값을 기준으로 활성/비활성을 관리한다.

기능 Method URL
사전 재료 비활성화 PATCH /api/v1/admin/products/{productId}/deactivate
사전 재료 재활성화 PATCH /api/v1/admin/products/{productId}/activate

엔티티에는 상태 변경 메서드를 추가했다.

public void deactivate() {
    this.isActive = false;
    this.updatedAt = LocalDateTime.now();
}

public void activate() {
    this.isActive = true;
    this.updatedAt = LocalDateTime.now();
}

서비스에서는 productId로 재료를 찾고, 없으면 ProductNotFoundException을 발생시킨다.
해당 예외는 공통 예외 핸들러에서 404 Not Found로 처리된다.

이 기능은 별도 요청 DTO가 필요하지 않았다.
변경 대상은 URL의 productId로 충분하고, 변경할 값은 API 목적에 따라 정해져 있기 때문이다.

#37 장보기 API 명세 오타 및 인증 여부 수정

장보기 API 명세에서 경로 오타와 인증 여부가 맞지 않는 부분을 정리했다.

특히 API-403의 URL에 itmes 오타가 있었고, 실제 구현 경로 기준으로 items로 수정해야 했다.

수정 전:
PATCH /api/v1/shopping-itmes/{shoppingItemId}/check

수정 후:
PATCH /api/v1/shopping-items/{shoppingItemId}/check

또한 장보기 항목 체크/체크 해제와 수정 API는 사용자 개인 데이터에 접근하는 기능이므로 인증이 필요하다.

API-403 인증 필요
API-404 인증 필요

추가로 백엔드 SecurityConfig에서도 예전 장보기 경로가 남아 있는지 확인했다.
실제 컨트롤러 경로는 /api/v1/shopping-items이므로 보안 설정도 이 기준과 맞아야 한다.

테스트와 검증

이번 작업에서 확인한 내용은 다음과 같다.

구분검증 내용
컴파일 ./gradlew compileJava
서비스 테스트 관리자 사전 재료 조회/추가/수정/비활성화/재활성화 테스트
Postman 관리자 토큰으로 사전 재료 API 정상 응답 확인
프론트 QA 장보기 추가/조회/체크/삭제/냉장고 반영 흐름 확인

관리자 API는 ADMIN 권한이 필요하기 때문에 Postman 테스트 시 일반 사용자 토큰이 아니라 관리자 토큰을 사용해야 했다.

정리

이번 작업을 통해 사용자용 API와 관리자용 API의 차이를 더 명확히 구분하게 됐다.

사용자용 사전 재료 검색 API는 활성 재료만 보여주면 되지만, 관리자용 API는 전체 재료와 비활성 재료까지 다뤄야 한다.
그래서 응답 DTO도 사용자용과 관리자용을 분리하는 편이 더 자연스러웠다.

또한 사전 재료 삭제를 실제 DELETE가 아니라 비활성화 방식으로 처리하면서, 서비스에서 데이터를 완전히 지우는 것과 상태값으로 관리하는 것의 차이도 정리할 수 있었다.

장보기 연동에서는 API 자체보다 화면 상태 갱신 흐름이 중요했다.
추가, 체크, 삭제, 냉장고 반영 이후 화면이 최신 데이터를 보여줘야 사용자는 기능이 정상적으로 동작한다고 느낀다.

이번 범위까지 진행하면서 냉장고와 장보기의 기본 사용자 흐름, 그리고 관리자 사전 재료 관리 흐름까지 어느 정도 연결됐다.