P3] 09. 맛집 List 만들기

uuuu.jini·2022년 1월 18일
0
post-thumbnail

목차

  1. 요구사항분석,프로젝트 설계
  2. Memory CRUD DB 개발
  3. Rest Api 개발
  4. Front View 개발

githun링크 : 현재 프로젝트 github링크

0. 요구사항 분석, 프로젝트 설계

네이버 검색 api 사용하여 , 검색한 맛집을 내 위시리스트에 넣고 방문 횟수 기록하거나 위시리스트에서 삭제하는 등의 기능을 제공할 예정이다. 이를 위해 , api 설계와 오픈 api 연동을 진행할 것이다. 이 프로젝트 부터는 github에 올려 관리해보려고 한다. git 사용에 익숙해지기 위함과 내가 무엇을 공부했는지 기록해보기 위함이다.

front 사용

Vue.js 사용할 예정이며, 현재 프론트를 완벽하게 학습할 수 없어 강사님이 주신 자료를 활용할 예정이다. 데이터베이스도 배우지 않아서 간단하게 만들어놓은 DB를 사용하여 설계해 나갈 것이다.


1. Memory CRUD DB 개발

save,find,delete,listall 등의 저장하고 찾아오고, 삭제,목록 전체 보기 등의 CRUD db 설계를 진행하였으며, 이는 나중에 JPA를 배우고 나서 더욱 효율적으로 설계가 가능할 예정이다. 관련 내용은 간단하게 코드 복습을 통해 공부하였다.


2. Rest Api 개발

네이버 검색 API 사용

우리는 오픈 api 를 사용하여 맛집 검색을 실행할 것이다. 그러기 위해 네이버 개발자 홈페이지에서 해당 api 사용을 위해 client id와 secret을 먼저 가져와야한다. 네이버 개발자 페이지에서 [Documents]->[서비스API]를 클릭하면 다양한 api와 사용법을 확인할수 있으며, id와 secret발급을 통해 사용이 가능하게 된다.
해당 api를 편하게 사용하기 위하여 우리는 resource하위에 해당 url과 id,secret을 yaml파일로 저장해두고 사용할 것이다.

yaml 파일?
처음 들어본 파일형식이었다. 검색해보니 밑의 형식과 같은 파일을 yaml(야믈)파일이라고 한다.

이러한 yaml파일에서 원하는 값들을 변수에 저장해두고 사용하려고 한다. 그러기 위해서는 @Value어노테이션을 사용하였다. 밑의 예시를 적어두었다.

    @Value("${naver.client.id}")
    private String naverClientId;

    @Value("${naver.client.secret}")
    private String naverClientSecret;

    @Value("${naver.url.search.local}")
    private String naverLocalSearchUrl;

    @Value("${naver.url.search.image}")
    private String naverImageSearchUrl;

value어노테이션의 인자로 해당 yaml파일에서의 변수를 가져올 위치를 .으로 연결하여 가져 올수 있다. 이때에 .은 하위로 내려가는 것을 의미한다. 즉 naver.client.id는 naver밑의 client밑의 id에 해당하는 값을 의미한다.

해당 api중 나는 지역 검색 api와 이미지 검색 api를 사용하려고 한다. 총 두개의 메서드를 사용하여 네이버 오픈 api에 접근하여 하나는 지역을 검색한 결과를 하나는 이미지를 검색한 결과를 받아오려고 한다. 강사님과 지역검색을 함께 만든 뒤 이미지 검색은 혼자서 실행해 보았다.

먼저 검색 메소드를 작성하기 전에 해당 요청 객체와 응답 객체를 클래스로 구현한뒤 사용하고자 하였다. 각 객체의 속성은 네이버 개발자 페이지의 documentation을 참고하여 작성하였다. 응답 객체에서는 변수 모두를 받지 않고 필요한 것들만 선택하여 받았다.

  • 요청 변수

  • 응답 변수

밑의 이미지 검색 예시를 적어보았다.

 public SearchImageRes searchImage(SearchImageReq searchImageReq){

        // 1. URI 만들기
        URI uri = UriComponentsBuilder.fromUriString(naverImageSearchUrl)
                .queryParams(searchImageReq.toMultiValueMap())
                .build()
                .encode()
                .toUri();

        //2. header 만들기
        var headers = new HttpHeaders();
        headers.set("X-Naver-Client-Id", naverClientId);
        headers.set("X-Naver-Client-Secret", naverClientSecret);
        headers.setContentType(MediaType.APPLICATION_JSON);

        // 3. request 객체 만들기
        var httpEntity = new HttpEntity<>(headers);

        // 4. response 타입 지정하기
        var responseType = new ParameterizedTypeReference<SearchImageRes>(){};

        // 5. restTemplate 요청
        var result = new RestTemplate().exchange(
                uri,
                HttpMethod.GET,
                httpEntity,
                responseType
        );

        return result.getBody();
    }

[uri 생성] -> [header 생성] -> [request 객체 생성] -> [response type 지정] -> [요청] -> [반환]

Service 제작

특정 쿼리를 받아서 검색을 진행한 후 결과를 리턴해주는 서비스를 개발하였다. 해당 메서드에서는 쿼리를 인자로 받고 결과값을 원하는 형태의 dto로 객체를 생성해둔 것을 반환해주는 형태로 만들었다. 쿼리에 대한 검색을 위해서는 네이버 오픈 api를 접근하여 지역검색과 이미지 검색을 실행하기 위해 위에서 정의한 메서드를 사용하였다.

이때, null값을 받아오지 않도록 total 수가 1개 이상인 경우에만 반환할 객체의 속성들을 설정하여 반환하였고, 그렇지 않은 경우에는 빈 객체를 반환하였다. 밑에 코드를 작성하였다.

@Service
@RequiredArgsConstructor
public class WishListService {

    private final NaverClient naverClient;

    public WishListDto search(String query){  //해당 쿼리를 받아서 검색한 결과를 반환해주는 서비스


        // 지역검색
        var searchLocalReq = new SearchLocalReq();
        searchLocalReq.setQuery(query);

        var searchLocalRes = naverClient.searchLocal(searchLocalReq); //해당 요청 query에 대한 지역검색 결과 받아오기
        if(searchLocalRes.getTotal() >0){ //지역검색한 결과가 있을 경우
            var localItem = searchLocalRes.getItems().stream().findFirst().get(); // 해당 검색결과의 첫번째 localItem 가져오기


            // image 검색 위한 쿼리 생성
            var imageQuery = localItem.getTitle().replaceAll("<[^>]*>",""); //필요없는 문자열 없애는 처리

            //해당 쿼리로 이미지 검색
            var searchImageReq = new SearchImageReq();
            searchImageReq.setQuery(imageQuery);

            var searchImageRes = naverClient.searchImage(searchImageReq);

            if(searchImageRes.getTotal()>0){

                var imageItem = searchImageRes.getItems().stream().findFirst().get();
                //해당 결과를 리턴
                var result = new WishListDto();
                result.setTitle(localItem.getTitle());
                result.setCategory(localItem.getCategory());
                result.setAddress(localItem.getAddress());
                result.setReadAddress(localItem.getRoadAddress());
                result.setImageLink(imageItem.getLink());
                result.setHomePageLink(localItem.getLink());


                return result;
            }
        }

        return new WishListDto();




    }


}

Controller 제작

1] 검색한 쿼리에 대한 결과를 반환 Controller

   @GetMapping("/search")  //검색한 쿼리 결과 반환 controller
    public WishListDto search(@RequestParam String query) {
        return wishListService.search(query);

    }

위의 search메서드를 활용하면 된다. 간단하다. 요청으로 받은 쿼리값을 넣어서 검색한 값을 결과로 반환해준다.

2] 위시리스트에 추가 Controller

추가이므로 postMapping을 사용한다. 추가할 dto객체를 받아서 service의 추가 메서드인 add메서드를 사용하여 database에 추가하여준다. 이때, db의 객체와 해당 dto의 객체가 서로 다르므로 변환해주는 메서드가 필요하다. 먼저 해당 컨트롤러를 작성하였다.

    //위시리스트 추가
    @PostMapping("")
    public WishListDto add(@RequestBody WishListDto wishListDto){
        log.info("{}",wishListDto);
        return wishListService.add(wishListDto);

    }

밑의 코드는 해당 서비스의 add 메서드이다.

    public WishListDto add(WishListDto wishListDto) { 
    
        var entity = dtoToEntity(wishListDto);
        var saveEntity = wishListRepository.save(entity);
        return entityToDto(saveEntity);

    }

dtoToEntityentityToDto는 이름과 동일하게 entity와 dto의 변환해주는 코드이다. 따로 작성해놓았다. [너무 노가다였음,,]

3] 전체 리스트를 불러오는 Controller

해당 db에 접근하여 모든 리스트를 불러오는 메서드를 작성하였다. 해당 db에 접근하여 가져오는 객체는 entity객체이므로 dto로 변환해주는 과정을 거쳤다.

    @GetMapping("/all")
    public List<WishListDto> findAll(){

        return wishListService.findAll(); // 전체 리스틀를 가져오는 메서드
    }

위의 코드는 controller내의 코드이다.
밑은 서비스의 findAll메서드이다.

    public List<WishListDto> findAll() {
        return wishListRepository.listAll()
                .stream()
                .map(it->entityToDto(it))
                .collect(Collectors.toList());
    }

4] 방문 기록 Controller

index를 받아서 해당 객체의 visitCount를 1씩 증가시켜주는 기능을 한다.

    //방문등록
    @PostMapping("/{index}")
    public void addVisit(@PathVariable int index){
         wishListService.addVisit(index);
    }
    
        public void addVisit(int index){
        var wishItem = wishListRepository.findById(index);
        if (wishItem.isPresent()) {
            var item = wishItem.get();
            item.setVisit(true);
            item.setVisitCount(item.getVisitCount()+1);
        }
    }

5] 삭제 Controller

index를 받아서 해당 객체를 삭제한다.

    //삭제
    @DeleteMapping("/{index}")
    public void delete(@PathVariable int index) {
        wishListService.delete(index);
    }
    
    
    public void delete(int index) {

        wishListRepository.deleteById(index);

    }

3. Front View 개발

강사님이 주신 자료를 이용하였습니다.

profile
멋쟁이 토마토

0개의 댓글