[예약사이트] 2. 가게 목록과 가게 상세 구현

2020. 10. 21. 10:15프로젝트/etc

위 프로젝트는 인강보면서 코드도 같이 치고 있습니다. 구체적인 API 메서드 활용보다 큰 덩어리 설계를 어떻게 하고 Spring 기능들은 어떻게 활용하는지에 중점을 두는 것이 미래 웹 사이트를 구현할 때 도움이 될 거라 생각합니다. 이번 글에서 다룰 프로그래밍적 지식은 Layer Architecture이며 DI는 Spring 복습 겸 조금만 다뤄보겠습니다. 

참고로 구현할 기능은 제목처럼 가게목록과 가게상세 입니다.

1) Layer Architecture

웹을 구현할 때 UI와 기능에 따라 객체들을 구분해 관리하는 객체 지향 프로그래밍 구조입니다. 즉 Controller 객체들은 interfaces package로 구분되며 코드를 최소화하여 DB와 WEB에 접근하는 단순명료한 코드들로 이루어져야 합니다.

서버와 변수 데이터 전달하는 VO 객체들과 정보들의 성격에 따라 개별 객체로 생성하는 Repository 인터페이스와 구현(Impl)객체들은 VO 객체들과 함께 domain package로 분류됩니다.

이런 domain package 객체들을 한데 묶어 Controller에 제공하는 메서드를 생성하는 Service 객체는 Repository 인터페이스들을 생성자에 의존주입 하며 application package에 담아줍니다.

의존 관계를 그림으로 풀어 나타낸다면 아래와 같이 설명할 수 있습니다. 

Controller 객체들은 Service 객체들에 의존적이며, ServiceRepository, VO 객체에 의존적입니다.

2) Controller 코드

Controller는 클라이언트의 요청을 처리하는 기능만 가진 깔끔한 객체여야 합니다. 보시다시피 코드가 가볍습니다. 전달 정보들과 메서드화의 처리는 Service에서 모두 실행해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package kr.co.yhdelivery.eatgood.interfaces;
 
import kr.co.yhdelivery.eatgood.application.RestaurantService;
import kr.co.yhdelivery.eatgood.domain.Restaurant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.List;
 
@RestController
public class RestaurantController {
 
    //자동으로 repository 객체 생성해서 Controller에 의존주입
    @Autowired
    private RestaurantService restaurantService;
 
    @GetMapping("/restaurants")
    //가게 목록
    public List<Restaurant> list() {
        List<Restaurant> restaurants = restaurantService.getRestaurants();
 
        return restaurants;
    }
 
    @GetMapping("/restaurants/{id}")
    //가게 상세
    public Restaurant detail(@PathVariable("id") Long id) {
        Restaurant restaurant = restaurantService.getRestaurant(id);
 
        return restaurant;
    }
}
 
cs

3) Service 코드

 @Service를 클래스 상단에 붙여줌으로써 Spring이 Service 객체를 찾을 수 있도록 합니다. 다만 의존 주입된 RestaurantRepository와 MenuItemRepository 클래스가 Interface인데 Interface를 참조한 이유는 국한된 메서드를 사용하기 위함입니다. 인터페이스 구현 클래스인 RestaurantRepositoryImpl 클래스를 참조한다면 만일 다른 클래스에서 인터페이스를 implement 한다면 그 메서드는 사용할 수 없기 때문입니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package kr.co.yhdelivery.eatgood.application;
 
import kr.co.yhdelivery.eatgood.domain.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class RestaurantService {
 
    @Autowired
    //Impl 클래스로 할 경우 제한되기 때문에 Interface를 참조 Interface 내 메서드만 사용
    private RestaurantRepository restaurantRepository;
 
    @Autowired
    //Impl 클래스로 할 경우 제한되기 때문에 Interface를 참조 Interface 내 메서드만 사용
    private MenuItemRepository menuItemRepository;
 
 
    public RestaurantService(RestaurantRepository restaurantRepository,
                             MenuItemRepository menuItemRepository) {
        this.restaurantRepository = restaurantRepository;
        this.menuItemRepository = menuItemRepository;
    }
 
    //가게 목록
    public List<Restaurant> getRestaurants() {
        List<Restaurant> restaurants = restaurantRepository.findAll();
        return restaurants;
    }
 
    //가게 상세
    public Restaurant getRestaurant(Long id) {
        Restaurant restaurant = restaurantRepository.findById(id);
 
        List <MenuItem> menuItems = menuItemRepository.findAllByRestaurantId(id);
        restaurant.setMenuTems(menuItems);
 
        return restaurant;
    }
 
}
 
cs

4) RepositoryImpl 코드

인터페이스 클래스는 메서드만 선언되어 있기 때문에 메서드를 구현한 구현 클래스 코드를 올렸습니다. Service에 의존 주입되는 domain 클래스는 상단에 @Component 라고 써줘야 Spring에서 인식 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//MenuItemRepository 구현 클래스
package kr.co.yhdelivery.eatgood.domain;
 
import org.springframework.stereotype.Component;
 
import java.util.ArrayList;
import java.util.List;
 
@Component
public class MenuItemRepositoryImpl implements MenuItemRepository {
 
    private List<MenuItem> menuItems = new ArrayList<>();
 
    public MenuItemRepositoryImpl() {
        menuItems.add(new MenuItem("Kimchi"));
    }
 
    @Override
    public List<MenuItem> findAllByRestaurantId(Long restaurantId) {
        return menuItems;
    }
}
 
//RestaurantRepository 구현 클래스
package kr.co.yhdelivery.eatgood.domain;
 
import org.springframework.stereotype.Component;
 
import java.util.ArrayList;
import java.util.List;
 
@Component
//Spring이 클래스를 관리하게 됨
public class RestaurantRepositoryImpl implements RestaurantRepository {
 
    List<Restaurant> restaurants = new ArrayList<>();
 
    public RestaurantRepositoryImpl() {
 
        restaurants.add(new Restaurant(1004L, "Bob zip""Seoul"));
        restaurants.add(new Restaurant(2020L, "Cyber Food""Seoul"));
    }
 
    @Override
    public List<Restaurant> findAll() {
        return restaurants;
    }
 
    @Override
    public Restaurant findById(Long id) {
 
        Restaurant restaurant = restaurants.stream()
                .filter(r -> r.getId().equals(id))
                .findFirst()
                .orElse(null);
 
        return restaurant;
    }
}
 
 
cs