@@ -19,11 +19,16 @@
 
 @RestController
 @Slf4j
+/*
+    (C) foodKind는 QueryParam으로 받는 것이 더 restful 해보입니다.
+    또한 showStoreInfo에서는 foodKind를 사용하지 않기 때문에, 개별 메서드에서 맵핑하는 것이 좋아 보입니다.
+*/
 @RequestMapping("/customer/{foodKind}/store")
 public class StoreController {
     private final MemberService memberService;
     private final StoreService storeService;
 
+    /* (C) lombok의 @RequiredArgsConstructor를 사용하면 좋을 것 같습니다. */
     public StoreController(MemberService memberService, StoreService storeService) {
         this.memberService = memberService;
         this.storeService = storeService;
@@ -33,11 +38,31 @@ public StoreController(MemberService memberService, StoreService storeService) {
     @ResponseStatus(HttpStatus.OK)
     public List<StorePartResponseDto> showStoreList(@RequestAttribute("cognitoUsername") String customerId,
                                                     @PathVariable FoodKind foodKind,
-                                                    HttpServletResponse response) throws IOException {
+                                                    HttpServletResponse response /* 사용하지 않는 param인 것 같습니다. */) throws IOException {
+        /*
+            1. (A) MemberService에 왜 coordinates를 획득하는 메서드가 있는 것인지 바로 이해하기는 살짝 어려웠습니다.
+            coordinates보다는 location이라는 단어가 다른 클래스들에서 사용하는 도메인 용어(Customer.location)와도 통일되고 더 이해하기 좋을 것 같습니다.
+
+            2. (C) Optional 타입은 획득하자마자 필요한 후처리(orElseThrow, isEmpty 등)를 해주어서
+            아래의 isEmpty와 같은 optional check 로직이 여러 코드에 퍼지지 않도록 하는 것이 좋다고 생각합니다.
+            MemberService.getCoordinates 내에서 orElseThrow 등으로 optional check & un-wrap하고, Point 타입으로 반환해주세요.
+        */
         Optional<Point> coordinates = memberService.getCoordinates(customerId);
         if(coordinates.isEmpty()){
+            /*
+                (R) NPE는 일반적으로 non-nullable한 곳에 null 값이 세팅되었을 때 발생하는 exception입니다.
+                Empty coordinates에 NPE를 던지면 의미의 혼동이 있을 것으로 보입니다.
+                자바 스펙인 NPE보다는, 프로젝트에서 empty의 의미를 갖는 exception을 custom하는 것이 좋아보입니다.
+                empty도 어떤 대상이 empty한지를 세분화하여 나타내면 더 유의미한 예외를 발생시킬 수 있을 것 같습니다.
+            */
             throw new NullPointerException("Customer has no location information.");
         }
+
+        /*
+            (A) 책 clean code에서는 collection의 자료형을 변수명에 붙이지 말 것을 권장합니다.
+            list -> set 등으로 collection 타입이 변경되면 storePartSet으로 변경해야 하는 경우때문에,
+            storeParts, stores와 같은 복수형으로 나타내기를 권장합니다.
+        */
         List<StorePartResponseDto> storePartList = new ArrayList<>();
         List<Store> storeList = storeService.getStoreListNearCustomer(coordinates.get(), foodKind);
         storeList.forEach(store -> {
@@ -48,6 +73,10 @@ public List<StorePartResponseDto> showStoreList(@RequestAttribute("cognitoUserna
 
     @GetMapping("/{storeId}")
     @ResponseStatus(HttpStatus.OK)
+    /*
+        (R) Store와 같이 db table에 저장되는 요소들은 최대한 클라이언트 단에서 분리하여 감춰주는 것이 좋은 것 같습니다.
+        일반적으로 Store에 대응되는 DTO를 만들어서 사용하는 편입니다.
+    */
     public StoreResponseDto showStoreInfo (@RequestAttribute("storeEntity") Store store,
                                            @PathVariable String storeId){
         return new StoreResponseDto(store);

리뷰: > Q) @RequiredArgConstructor를 Entity에서 사용하는 경우 입력된 Property의 순서를 바꿨을 때 때 같은 타입의 property라면 문제가 되는 경우가 있더라고요. 생성자를 생성할 때 매개변수 입력 순서를 바꾸지 않아도 컴파일 과정에서 에러가 발생하지 않아서 잘못된 값이 들어와도 캐치할 수 없어서 아예 @RequiredArgConstructor를 사용하지 말자고 하는 글도 봤었습니다. 물론 Controller라서 같은 타입의 property가 없으니까 여기선 문제가 없겠지만, 실제로 @requiredargsconstructor를 편의상 많이 사용하나요? Entity에서만 사용하지 않으면 되는 건가요?

말씀하시는 문제가 어떤 문제인지 알 수 있을까요?

---

> Q) Optional 사용에서 항상 고민이 되었는데, Service 로직 단에서 orElseThrow()를 던지는게 낫다고 보시나요? orElse(null)과 같은 방식은 어떻게 생각하시나요? exception을 throw하는게 memory cost가 많이 들어간다고 알고 있어서 다른 방식은 어떤지 궁금합니다.

orElseThrow, orElse, orElseGet 등으로 optional은 받은 쪽에서 최대한 빠르게 벗겨내는 것이 좋다고 생각합니다.
optional 체크 로직이 괜히 여기저기 돌아다니면 해당 데이터가 nullable한 상태임을 계속 고려해야 하는데, 그렇게 하기보다는 empty이든 아니든 빨리 optional wrapping을 제거하려고 합니다.

exception이 memory를 많이 잡아먹는다는 것은 아마 슬랙에 공유된 질문에서 본 것 같은데, 메모리 문제로 exception을 던지는 것까지 고려해야할 수준의 애플리케이션이라면 차라리 해당 기능을 마이크로 서비스로 분리해서 golang이나 rust, scala 등 특화 언어로 접근하는 것이 맞는 것 같습니다. exception throw는 자바에서 best practice라고 생각해서 ㅎㅎ 저는 exception을 던지지 못할 정도까지는 본 적이 없는 것 같네요

---

> Q) showStoreInfo 메서드의 @RequestAttribute의 Store 객체는 StoreCheckInterceptor로부터 넘겨받은 객체입니다. 클라이언트는 pathvariable로 storeId만 던져주고 있어요. 인터셉터로 받은 Store인데도 DTO를 사용하는 게 좋다고 보세요?

흠 그렇군요.. 저는 인터셉터에서 storeId를 store로 변환해서 controller에 넘겨주는 부분이 어떠한 이유로 있는 것인지 궁금하긴 합니다 ㅎㅎ storeId의 용도가 컨트롤러 내에서 변경될 수도 있을텐데, controller가 interceptor에 강하게 결합된 느낌이 드는 것 같은데 다른 분들은 어떻게 생각하는지 들어보시면 좋을 것 같네요~