본문 바로가기
JAVA

스프링 4주차 강의내용 정리

by 너무앵 2021. 12. 24.

강의 목표

기본 코드 작성 숙달

  1. 3계층 숙달(Controller, Service, Repository)
    • 개발의 핵심 - 분업 & 느슨한 결합(유연성 및 확장성 확보)
    • 3계층(Controller, Service, Repository)을 손에 익을 때까지 연습
      • Controller : 제일 바깥 쪽에서 요청을 받고, 응답을 되돌려주는 역할
      • Service : 구체적인 작업 순서를 결정. (Controller ↔ Repository)
      • Repository : DB와 연결되어 CRUD 실시
      • 그리고 각 레이어 간에는 절대 Entity를 직접 사용하지 않고, DTO 사용
  2. API handling
    • 실제 직접 코딩할 필요 없이 API를 이용하여 정해진 약속대로 요구하면 결과 수신 가능
    • API 사용법 연습

네이버 쇼핑 API 사용하기

REST API - GET / POST / PUT / DELETE 를 통해 JSON 형식으로 자료를 받을 수 있음

  • 네이버 쇼핑 API 설명문서
  • 쇼핑 API를 신청한 뒤 ARC를 이용하여 값을 조회할 수 있음
  • ARC를 통하여 원하는 프로그래밍 언어(이 경우 JAVA)로 API를 쓸 수 있는 코드로 변환 가능

**** 프로젝트 생성시 Preference에서 auto import와 complier 세팅 필수**

(add unambiguous import ~~) 와 (Enable annotation processing)


API 연동하기

  1. API를 가져오기 위한 ‘NaverShopSearch’ 클래스를 생성
public class NaverShopSearch {
    public String search() {

		// 이 곳에 ARC에서 변환한 코드를 붙여넣는다.

    }

    public static void main(String[] args) {
        NaverShopSearch naverShopSearch = new NaverShopSearch();
        naverShopSearch.search();
    }
}
public class NaverShopSearch {
    public String search() {

			*RestTemplate rest = new RestTemplate();
			HttpHeaders headers = new HttpHeaders();
			headers.add("X-Naver-Client-Id", "네이버 API 클라이언트 아이디");
			headers.add("X-Naver-Client-Secret", "네이버 API 시크릿 코드");
			String body = "";

			HttpEntity requestEntity = new HttpEntity(body, headers);
			ResponseEntity responseEntity = rest.exchange("<https://openapi.naver.com/v1/search/shop.json?query=아디다스>", HttpMethod.GET, requestEntity, String.class);
			HttpStatus httpStatus = responseEntity.getStatusCode();
			int status = httpStatus.value();
			String response = responseEntity.getBody();
			System.out.println("Response status: " + status);
			System.out.println(response);*
			return response; // 원하는 값을 가진(String) response를 리턴
    }

    public static void main(String[] args) {
        NaverShopSearch naverShopSearch = new NaverShopSearch();
        naverShopSearch.search();
    }
}
  1. Project 설계하기

API 설계

  1. 3계층 설계하기
    • ProductRestController: 관심 상품 관련 컨트롤러
    • SearchRequestController: 검색 관련 컨트롤러
    URL이 api/products 와 api/search 로 나뉘었기 때문에 Controller도 두가지로 나눠야함.
    • ProductService: 관심 상품 가격 변경
    Repository</aside>
    • Product: 관심 상품 테이블
    • ProductRepository: 관심 상품 조회, 저장
    • ProductRequestDto: 관심 상품 등록하기
    • ProductMypriceRequestDto: 관심 가격 변경하기
    • ItemDto: 검색 결과 주고받기
  2. <aside> 👉 여기서 DB에 저장되는 녀석은 Product 뿐이라는 점!
  3. Service
  4. Controller

프로젝트 필요 기능

  1. 키워드로 상품 검색 후 목록으로 보여주기
  2. 관심 상품 등록
  3. 관심 상품 조회
  4. 관심 상품에 원하는 가격 등록 후 가격보다 낮은 경우 화면에 띄워주기

세부 코드 작성

#1. Timestamped

Getter / MappedSuperclass / EntityListeners(AuditingEntityListener.class) 추가 할 것

**@Getter** // get 함수를 자동 생성
**@MappedSuperclass** // 멤버 변수가 컬럼이 되도록 함
**@EntityListeners**(AuditingEntityListener.class) // 변경되었을 때 자동으로 기록

public abstract class Timestamped { //abstract 클래스로 선언
    @CreatedDate // 최초 생성 시점
    private LocalDateTime createdAt;

    @LastModifiedDate // 마지막 변경 시점
    private LocalDateTime modifiedAt;
}

#2. Product 클래스

#3. Repository 인터페이스 생성 (JpaRepository extend 해주기)

#4. Controller 만들어주기

public class ProductRestController {

    // Get방식 Mapping
    @GetMapping("/api/products")
    public List<Product> getProducts(){
        return productRepository.findAll(); // productRepository에서 findAll하라고 명령
    }
**// 근데 문제는 productRepository 선언을 안해줬으니까 멤버 변수를 선언해줘야함**
}
@RequiredArgsConstructor // 이거까지해줘야 에러 안남
public class ProductRestController {

    final ProductRepository productRepository; // productRepository는 꼭 필요

    // Get방식 Mapping
    @GetMapping("/api/products")
    public List<Product> getProducts(){
        return productRepository.findAll();
    }
}

#5. Dto생성하기

ProductMypriceRequestDto / ProductRequestDto 생성

#6 Product 클래스에 DTO 추가하기

public Product(ProductRequestDto requestDto){
        this.title = requestDto.getTitle();
        this.link = requestDto.getLink();
        this.lprice = requestDto.getLprice();
        this.image = requestDto.getImage();
				this.myprice = 0; // 최초 값은 0
    }

#7 Service 클래스 생성

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository productRepository; 

    @Transactional // **이거 필수**
    public Long update(Long id, ProductMypriceRequestDto requestDto){
        Product product = productRepository.findById(id).orElseThrow(
                ()-> new IllegalArgumentException("아이디가 존재하지 않습니다")
        );
        product.update(requestDto); // **product 클래스에서 update하라고 토스**
        return id;
    }
}
public void update(ProductMypriceRequestDto requestDto){
        this.myprice = requestDto.getMyprice();
    }
// Product 클래스에서 update 메소드를 생성
// 

#8. PostMapping

@PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto){
        Product product = new Product(requestDto);
        return productRepository.save(product);
    }

#9 검색어 바꾸기

public class NaverShopSearch {
    public String search(String query) { // 검색어 query 스트링 넣기;

			*RestTemplate rest = new RestTemplate();
			HttpHeaders headers = new HttpHeaders();
			headers.add("X-Naver-Client-Id", "네이버 API 클라이언트 아이디");
			headers.add("X-Naver-Client-Secret", "네이버 API 시크릿 코드");
			String body = "";

			HttpEntity requestEntity = new HttpEntity(body, headers);
			ResponseEntity responseEntity = rest.exchange("<https://openapi.naver.com/v1/search/shop.json?query=>" +* query*, HttpMethod.GET, requestEntity, String.class);
			HttpStatus httpStatus = responseEntity.getStatusCode();
			int status = httpStatus.value();
			String response = responseEntity.getBody();
			System.out.println("Response status: " + status);
			System.out.println(response);*
			return response; // 원하는 값을 가진(String) response를 리턴
    }

    public static void main(String[] args) {
        NaverShopSearch naverShopSearch = new NaverShopSearch();
        naverShopSearch.search();
    }
}

#10. 검색결과를 문자열에서 DTO로 바꾸기

public static void main(String[] args) {
        NaverShopSearch naverShopSearch = new NaverShopSearch();
        String result = naverShopSearch.search("z폴드3");

        //JSON오브젝트가 모여서 JSON어레이가 됨
       JSONObject rjson = new JSONObject(result); // 검색어로 찾은 값을 result로 받아와서 rjson으로 선언
        JSONArray items = rjson.getJSONArray("items"); // rjson에서 items를 키값으로 가지는 리스트(items)를 가져온다

        for(int i = 0; i < items.length(); i++){ // items 안에 들어있는 것들을 하나씩 빼올 것것
           JSONObject itemJson = items.getJSONObject(i); //items에서 Json오브젝트를 꺼낸다 (i번째)
        }
    }
**// JSON 라이브러리를 이용해서 JSON으로 된 데이터를 꺼내온다**

for(int i = 0; i < items.length(); i++){ // items 안에 들어있는 것들을 하나씩 빼올 것것
           JSONObject itemJson = items.getJSONObject(i); //items에서 Json오브젝트를 꺼낸다 (i번째)
            String title = itemJson.getString("title");
            String image = itemJson.getString("image");
            int lprice = itemJson.getInt("lprice");
            String link = itemJson.getString("link");
        }

**//itemJson에서 필요한 데이터를 골라서 각각의 변수에 get해서 집어넣어준다
//itemJson에서 key값이 "title"인 애들을 title 변수에 넣고 뭐 이런식**

#11. itemDto 생성

@Getter
public class ItemDto {
    private String title;
    private String link;
    private String image;
    private int lprice;

    public ItemDto(JSONObject itemJson) {
        this.title = itemJson.getString("title");
        this.link = itemJson.getString("link");
        this.image = itemJson.getString("image");
        this.lprice = itemJson.getInt("lprice");
    }
}
public List<ItemDto> fromJSONtoItems(String result){
            //JSON오브젝트가 모여서 JSON어레이가 됨
            JSONObject rjson = new JSONObject(result); // 검색어로 찾은 값을 result로 받아와서 rjson으로 선언
            JSONArray items = rjson.getJSONArray("items"); // rjson에서 items를 키값으로 가지는 리스트(items)를 가져온다

            List<ItemDto> itemDtoList = new ArrayList<>(); // itemDto를 담아줄 리스트 선언

            for(int i = 0; i < items.length(); i++){ // items 안에 들어있는 것들을 하나씩 빼올 것것
                JSONObject itemJson = items.getJSONObject(i); //items에서 Json오브젝트를 꺼낸다 (i번째)
                ItemDto itemDto = new ItemDto(itemJson);
                itemDtoList.add(itemDto);
            }
            return itemDtoList;
        }

#12 컴포넌트 등록하기

<aside> 👉 더 이상 검색을 main 메소드에서 진행하는게 아니라, Controller 에서 가져다 씀

스프링이 자동으로 필요한 클래스를 필요한 곳에 생성하려면, "아, 사용자가 요구하면 자동으로 생성할 클래스 목록이 이것이구나" 라고 확인할 수 있어야함

이를 위해 필요한 것이 Component 등록

</aside>

@Component // 컴포넌트 등록
public class NaverShopSearch {
    public String search(String query) {

#13 SearchRequestDto 생성하기

NaverShopSearch를 컴포넌트 등록을 했으니 스프링이 자유롭게 가져다가 쓸 수 있음

@RequiredArgsConstructor // final 로 선언된 클래스를 자동으로 생성
@RestController // JSON으로 응답함을 선언
public class SearchRequestController {

    private final NaverShopSearch naverShopSearch;

    @GetMapping("/api/search")

		// 최종 itemDto를 출력하는 메소드 선언 / @RequestParam를 넣어줘야 됨
    public List<ItemDto> execSearch(@RequestParam String query){
		// navershopsearch를 컴포넌트 등록했으니까 막 갖다쓰면 됨
		// 파라미터 query를 가져와서 search한걸 result에 담기
        String result = naverShopSearch.search(query);

		// navershopsearch에 있는 fromJSONtoItems 메소드를통해 원하는걸 받아냄
        return naverShopSearch.fromJSONtoItems(result);
    }
}

#14 addProduct 생성하기

function addProduct(itemDto) {
    /**
     * modal 뜨게 하는 법: $('#container').addClass('active');
     * data를 ajax로 전달할 때는 두 가지가 매우 중요
     * 1. contentType: "application/json",
     * 2. data: JSON.stringify(itemDto),
     */
    // 1. POST /api/products 에 관심 상품 생성 요청
    $.ajax({
        type: "POST",
        url: '/api/products',
        contentType: "application/json",
        data: JSON.stringify(itemDto),
        success: function (response) {
            // 2. 응답 함수에서 modal을 뜨게 하고, targetId 를 reponse.id 로 설정 (숙제로 myprice 설정하기 위함)
            $('#container').addClass('active');
            targetId = response.id;
        }
    })
}

#15 showProduct, addProductItem 만들기

function showProduct() {
    /**
     * 관심상품 목록: #product-container
     * 검색결과 목록: #search-result-box
     * 관심상품 HTML 만드는 함수: addProductItem
     */
    
    // 1. GET /api/products 요청
    $.ajax({
        type: 'GET',
        url: '/api/products',
        success: function (response) {
            // 2. 관심상품 목록, 검색결과 목록 비우기
            $('#product-container').empty();
            $('#search-result-box').empty();
            // 3. for 문마다 관심 상품 HTML 만들어서 관심상품 목록에 붙이기!
            for (let i = 0; i < response.length; i++) {
                let product = response[i];
                let tempHtml = addProductItem(product);
                $('#product-container').append(tempHtml);
            }
        }
    })
}

function addProductItem(product) {
    // link, image, title, lprice, myprice 변수 활용하기
    return `<div class="product-card" onclick="window.location.href='${product.link}'">
                <div class="card-header">
                    <img src="${product.image}"
                         alt="">
                </div>
                <div class="card-body">
                    <div class="title">
                        ${product.title}
                    </div>
                    <div class="lprice">
                        <span>${numberWithCommas(product.lprice)}</span>원
                    </div>
                    <div class="isgood ${product.lprice > product.myprice ? 'none' : ''}">
                        최저가
                    </div>
                </div>
            </div>`;
}
  • 3항 조건문
${product.lprice > product.myprice ? 'none' : ''}
// 조건문? 참일 경우 출력: 거짓일 경우 출력

 

 

 

 

'JAVA' 카테고리의 다른 글

스프링 3주차 강의내용 정리  (0) 2021.12.16
스프링 2주차 강의내용 정리  (0) 2021.12.12
스프링 1주차 강의내용 정리  (2) 2021.12.01