연습용 주문 서버 만들기
등록 API는 따로 만들지 않고, 메뉴가 고정되어 있다고 가정하겠습니다.
- 메뉴 조회는 누구나 가능하다.
- 프로젝트 실행 시 기본 메뉴 생성
역시 매우 간단한 기능이므로 딱히 부연 설명할 게 없습니다.
메뉴 조회 API Controller
@RestController
@RequestMapping(V1_API_PREFIX)
class MenuViewApiController(
private val menuService: MenuService
) {
@GetMapping("/menu")
fun getAllMenu(): ResponseEntity<List<MenuViewResponse>> {
val allMenu = menuService.getAllMenu();
return ResponseEntity(allMenu, HttpStatus.OK)
}
}
메뉴 조회 GET 요청을 받으면 MenuService
에서 모든 메뉴를 불러옵니다.
메뉴 조회 ResponseDTO
data class MenuViewResponse(
val id: Long,
val menu: String,
val price: Int
) {
companion object {
internal fun of(menu: Menu) = MenuViewResponse(
id = menu.id,
menu = menu.menu,
price = menu.price
)
}
}
반환은 이런 형태로 하게됩니다.
메뉴 조회 Service
interface MenuService {
fun getAllMenu(): List<MenuViewResponse>
}
@Service
internal class MenuDefaultService(
private val menuViewService: MenuViewService
): MenuService{
@Transactional(readOnly = true)
override fun getAllMenu(): List<MenuViewResponse> {
return menuViewService.getAllMenu()
}
}
@Service
internal class MenuViewService(
private val menuRepository: MenuRepository
) {
@Transactional(readOnly = true)
fun getAllMenu(): List<MenuViewResponse> {
return menuRepository.findAll()
.map { MenuViewResponse.of(it) }
}
}
- 읽기만 하기 때문에 트랜잭션을 읽기 전용으로 선언했습니다.
웹 레이어와 비즈니스 레이어를 분리하고 반드시 인터페이스를 통해서만 접근하도록 설계했습니다.
웹 계층에서는 비즈니스 계층에 있는 구현체를 참조할 수 없습니다. 따라서 구현체를 좀더 자유롭게 변경할 수 있습니다.
Repository/Entity
@Entity
internal class Menu(
menu: String,
price: Int
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0
val menu: String = menu
val price: Int = price
val createdAt: LocalDateTime = LocalDateTime.now()
}
internal interface MenuRepository: JpaRepository<Menu, Long> {
}
엔티티는 최대한 간단하게 메뉴 이름과 가격으로만 구성했습니다.
Id를 기본값을 0을 준 이유는 kotlin 문법을 우회하기 위해서입니다. JPA는 기본값이 설정되어 있어도 (어떻게 그러는지는 모르겠지만) Repository에 save할 때 id를 초기화합니다.
서버 실행 시 기본 메뉴 생성
@Component
internal class MenuInitializer(
private val menuRepository: MenuRepository
) {
@PostConstruct
fun init() {
if (menuRepository.count() > 0) return
val menuList = listOf(
Menu("불고기 샌드위치", 8000),
Menu("닭가슴살 샌드위치", 8000),
Menu("함박 샌드위치", 8000),
Menu("에그마요 샌드위치", 6000),
Menu("햄참치 샌드위치", 7000),
Menu("장조림 라이스 샐러드", 10000),
Menu("잠봉 라이스 샐러드", 10000),
Menu("연어 샐러드", 12000),
Menu("아메리카노", 3000),
Menu("카페라떼", 4000),
Menu("히비스커스티", 3000),
Menu("탄산음료", 2000),
Menu("생수", 1000),
)
menuRepository.saveAll(menuList)
}
}
나중에 어떤 메뉴를 사면 음료를 할인해주는 정책 같은 것을 추가해봐도 될 것 같습니다.
Test
@AutoConfigureMockMvc
@SpringBootTest
internal class MenuViewApiControllerTest {
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `메뉴 목록 조회 200 OK`() {
mockMvc.get("$V1_API_PREFIX/menu")
.andExpect {
status { isOk() }
content {
contentType(MediaType.APPLICATION_JSON)
}
} .andDo { print() }
}
}
사실 로직이라고 할 것이 마땅치 않아서 간단히 호출이 되고, 원하는 응답이 돌아오는지 정도만 확인했습니다.
'Spring' 카테고리의 다른 글
연습용 주문 서버 만들기 02 멤버십 발급 API 작성하기 (0) | 2023.01.15 |
---|---|
연습용 주문 서버 만들기 01 Kotlin/Spring 멀티모듈 프로젝트 구성 (2) | 2022.12.22 |
Spring Scheduler 테스트 하기 (0) | 2022.11.28 |
Springboot 통합 테스트로 불안한 리팩토링에서 벗어나기 (2) | 2022.08.24 |
ConstraintValidator 그리고 유효성 검사에 대한 고민.. (0) | 2022.05.16 |