- 이제 사용자 기능까지 어느정도 끝났기 때문에,
- 매뉴를 주문할 가게에 대해서 만들어 보겠다
StoreEntity
- 가게는 기본적으로 이름, 주소, 상태(enum), 카테고리(enum)
- 좋아요수, 가게 섬네일 주소, 최소 금액, 최수 주문 금액, 그리고 가게 번호 이렇게 필드를 지정하였다
@Entity
@Table(name = "store")
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Getter
@EqualsAndHashCode(callSuper = true)
public class StoreEntity extends BaseEntity {
@Column(length = 100,nullable = false)
private String name;
@Column(length = 150,nullable = false)
private String address;
@Column(length = 50,nullable = false)
@Enumerated(EnumType.STRING)
private StoreStatus status;
@Column(length = 50,nullable = false)
@Enumerated(EnumType.STRING)
private StoreCategory category;
private double star;
@Column(length = 200, nullable = false)
private String thumbnailUrl;
@Column(precision = 11,scale = 4,nullable = false)
private BigDecimal minimumAmount;
@Column(precision = 11,scale = 4,nullable = false)
private BigDecimal minimumDeliveryAmount;
@Column(length = 20)
private String phoneNumber;
StoreStatus
@AllArgsConstructor
public enum StoreStatus {
REGISTERED("등록"),
UNREGISTERED("헤지");
private String description;
}
StoreCategory
@AllArgsConstructor
public enum StoreCategory {
CHINESE_FOOD("중식","중식"),
WESTERN_FOOD("양식","양식"),
KOREAN_FOOD("한식","한식"),
JAPANESE_FOOD("일식","일식"),
CHICKEN("치킨","치킨"),
PIZZA("피자","피자"),
HAMBURGER("함버거","햄버거"),
COFFEE_TEA("커피&차","커피&차");
private String display;
private String description;
}```
## StoreRepository
```java
@Repository
public interface StoreRepository extends JpaRepository<StoreEntity,Long> {
Optional<StoreEntity> findFirstByIdAndStatusOrderByIdDesc(Long id, StoreStatus status);
List<StoreEntity> findAllByStatusOrderByIdDesc(StoreStatus status);
List<StoreEntity> findAllByStatusAndCategoryOrderByStarDesc(StoreStatus status, StoreCategory storeCategory);
}
- 등록이 되어있고, 가게 아이디를 통해 찾는 기능과
- 상태가 등록으로 되어있으면 모두 찾는 기능
- 등록으로 되어있고, 특정 카테고리를 입력하면 모두 받는 기능 이렇게 추가적으로 만들어 줬다
- 이제 화면을 나타내는 api 모듈로 이동하자
- domian 패키지에 store 패키지를 만들고
- 컨트롤러, 서비스, 컨버터, 비지니스를 만들어준다!

StroeService
@Service
@RequiredArgsConstructor
public class StoreService {
private final StoreRepository storeRepository;
public StoreEntity getStoreWithThrow(Long id){
return storeRepository.findFirstByIdAndStatusOrderByIdDesc(id, StoreStatus.REGISTERED)
.orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));
}
@Transactional
public StoreEntity register(StoreEntity storeEntity){
return Optional.ofNullable(storeEntity)
.map(it -> {
it.starAndStatus(0,StoreStatus.REGISTERED);
return storeRepository.save(it);
})
.orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));
}
public List<StoreEntity> searchByCategory(StoreCategory storeCategory){
List<StoreEntity> list = storeRepository.findAllByStatusAndCategoryOrderByStarDesc(
StoreStatus.REGISTERED,
storeCategory
);
return list;
}
public List<StoreEntity> registerStore(){
List<StoreEntity> list = storeRepository.findAllByStatusOrderByIdDesc(StoreStatus.REGISTERED);
return list;
}
public Optional<StoreEntity> findById(Long id){
return storeRepository.findById(id);
}
}
- 순수 store에 대한 서비스를 알아보자
- 가게를 등록하는
register 메서드이다
- entity에 setter을 지양하기 위해서,
- star에 0을 넣고, 상태를 REGISTERD로 지정해주는
starAndStatus라는 메서드를
- 엔티티 레벨에 만들어주었다
public void starAndStatus(double star,StoreStatus status){
this.star = star;
this.status=status;
}
- 먼저 아까 만든 레파지토리를 이용해, id와 REGISTERED상태인 StoreEntity를 반환하는
getStoreWithThorw
- 카테고리를 이용해서 동륵된 리시트를 찾는
searchByCategory
- 등록된 가게들을 모두 가져오는
regisgerStore
- 가게 id를 이용해 가게를 찾는
findbyId를 만들어 주었다
StoreRegisterRequest
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StoreRegisterRequest {
@NotBlank
private String name;
@NotBlank
private String address;
@NotNull
private StoreCategory storeCategory;
@NotBlank
private String thumbnailUrl;
@NotNull
private BigDecimal minimumAmount;
@NotNull
private BigDecimal minimumDeliveryAmount;
@NotBlank
private String phoneNumber;
}
- 등록할때는, 다음 요청에 필요한 데이터를 받고
StoreResponse
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class StoreResponse {
private Long id;
private String name;
private String address;
private StoreStatus status;
private StoreCategory category;
private double star;
private String thumbnailUrl;
private BigDecimal minimumAmount;
private BigDecimal minimumDeliveryAmount;
private String phoneNumber;
}
- 응답할때에는, 다음 reponse dto를 사용한다
StoreConverter
- 요청 데이터와, 응답 데이터의 형식은 정해졌고!
- 이제 repository에서 저장할려면, registgerRequest -> StoreEntity형식으로 바꾸고
- 응답 데이터 형식으로 entity -> storeResponse형식으로 바꾸는 메서드 두개를 만들어준다
builder()패턴을 이용한다
@Converter
public class StoreConverter {
public StoreEntity toEntity(StoreRegisterRequest request){
return Optional.ofNullable(request)
.map(it -> {
return StoreEntity.builder()
.name(request.getName())
.address(request.getAddress())
.category(request.getStoreCategory())
.minimumAmount(request.getMinimumAmount())
.minimumDeliveryAmount(request.getMinimumDeliveryAmount())
.thumbnailUrl(request.getThumbnailUrl())
.phoneNumber(request.getPhoneNumber())
.build();
}).orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));
}
public StoreResponse toResponse(StoreEntity entity){
return Optional.ofNullable(entity)
.map(it -> {
return StoreResponse.builder()
.id(entity.getId())
.name(entity.getName())
.status(entity.getStatus())
.address(entity.getAddress())
.category(entity.getCategory())
.minimumAmount(entity.getMinimumAmount())
.minimumDeliveryAmount(entity.getMinimumDeliveryAmount())
.thumbnailUrl(entity.getThumbnailUrl())
.phoneNumber(entity.getPhoneNumber())
.star(entity.getStar())
.build();
}).orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));
}
}
StoreBusiness
@Business
@RequiredArgsConstructor
public class StoreBusiness {
private final StoreService storeService;
private final StoreConverter storeConverter;
public StoreResponse register(StoreRegisterRequest storeRegisterRequest){
StoreEntity entity = storeConverter.toEntity(storeRegisterRequest);
StoreEntity newEntity = storeService.register(entity);
StoreResponse response = storeConverter.toResponse(newEntity);
return response;
}
public List<StoreResponse> searchCategory(StoreCategory category){
List<StoreEntity> storeEntities = storeService.searchByCategory(category);
List<StoreResponse> storeResponses = storeEntities.stream()
.map(storeEntity -> {
return storeConverter.toResponse(storeEntity);
})
.collect(Collectors.toList());
return storeResponses;
}
}
- 비지니스 영역에는, store 등록하고 response형태로 보내는 로직과
- category를 넣으면, 그 cateogry에 맞는 가게들을 모두 내보내는 메서드
- 총 2개를 만들었다!
StoreOpenApiContorller
- 먼저 로그인 인터셉터를 거치지 않는 openApiController에서는
Api<StoreResponse>형태로 성공으로 반환을 해줘야한다
Api<StoreRegisterRequest>형태로 요청이 들어오고, 여기서 body를 꺼낸 후, 등록을 하고
Api<StoreResponse>형태로 Api.OK로 반환을 해준다
@RequiredArgsConstructor
@RestController
@RequestMapping("/open-api/store")
public class StoreOpenApiController {
private final StoreBusiness storeBusiness;
@PostMapping("/register")
public Api<StoreResponse> register(@Valid @RequestBody Api<StoreRegisterRequest> request){
StoreResponse register = storeBusiness.register(request.getBody());
return Api.OK(register);
}
}
StoreApiController
- api로그인이 끝나고, token인증을 받은 페이지이다
- 비지니스 로직에서
searchCategory를 이용해
- 카테고리를 입력하면, 결과릴 리스트로 만든 후에, API요청 스펙에 맞춰서 내려준다
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/store")
public class StoreApiController {
private final StoreBusiness storeBusiness;
@GetMapping("/search")
public Api<List<StoreResponse>> search(@RequestParam(required = false,value = "storeCategory") StoreCategory storeCategory){
List<StoreResponse> response = storeBusiness.searchCategory(storeCategory);
return Api.OK(response);
}
}
실행
- 다음과 같이 가게를 등록하고

- 원하는 형태의 api형식의 응답 메시지가 나간 것을 볼 수 있다
- 이제 원하는데로, 카테고리를 입력해 데이터를 받으려면

- 다음과 같이 요청하면
{
"result": {
"result_code": 200,
"result_message": "성공",
"result_description": "성공"
},
"body": [
{
"id": 1,
"name": "starbucks_강남점",
"address": "서울시 강남구",
"status": "REGISTERED",
"category": "COFFEE_TEA",
"star": 0,
"thumbnail_url": "https://i.namu.wiki/i/95ZuIdMicr_HrM1pbIcFD3DLfvKxuyhjf3xKsHT5tlddl2uoSm_L2gp0sBL6iO7meZJqb9KMcQUiFEpnFQZ0G-GKIUkyLHOe1LppL3z34p9SURyQCg4LLSYQFmJRVKIIAR_D0wLqmkgl6VBvwanK-w.webp",
"minimum_amount": 8000,
"minimum_delivery_amount": 3000,
"phone_number": "010-1234-5678"
},
{
"id": 2,
"name": "스타벅스_강남",
"address": "서울 강남",
"status": "REGISTERED",
"category": "COFFEE_TEA",
"star": 0,
"thumbnail_url": "https://i.namu.wiki/i/95ZuIdMicr_HrM1pbIcFD3DLfvKxuyhjf3xKsHT5tlddl2uoSm_L2gp0sBL6iO7meZJqb9KMcQUiFEpnFQZ0G-GKIUkyLHOe1LppL3z34p9SURyQCg4LLSYQFmJRVKIIAR_D0wLqmkgl6VBvwanK-w.webp",
"minimum_amount": 8000,
"minimum_delivery_amount": 3000,
"phone_number": "010-1234-5678"
},
{
"id": 4,
"name": "별다방",
"address": "제주도",
"status": "REGISTERED",
"category": "COFFEE_TEA",
"star": 0,
"thumbnail_url": "string",
"minimum_amount": 4500,
"minimum_delivery_amount": 3500,
"phone_number": "010-8888-8888"
}
]
}
- 우리가 원하는 형식으로 데이터가 들어오고
- id 4번에 우리가 방금 추가한 별다방이 함께 들어오는 것을 알 수 있다