본문 바로가기

웹 개발/스프링

[스프링 부트 기초] 스프링 나만의 셀렉샵

0. 만들거 예시

http://spring.spartacodingclub.kr/

 

00만의 셀렉샵

관심상품을 선택하고, 최저가 알림을 확인해보세요!

spring.spartacodingclub.kr

프로젝트 셋팅 : 

패키지 : Spring Web, MySQL, H2, Lombok, JPA

셋팅 : Annotation Processing(어노테이션 프로세서), Auto Import(자동 가져오기)

 

1. 네이버 쇼핑 API 

네이버 API 목록 : 

https://developers.naver.com/products/intro/plan/

 

https://developers.naver.com/products/intro/plan/

 

developers.naver.com

 

내 어플리케이션 등록 : 

https://developers.naver.com/apps/#/wizard/register

 

검색 API 추가 : WEB, http://localhost 입력

 

네이버 쇼핑 API 설명 문서 : 

https://developers.naver.com/docs/serviceapi/search/shopping/shopping.md

 

검색 > 쇼핑 - Search API

검색 > 쇼핑 쇼핑 검색 개요 개요 검색 API와 쇼핑 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 영화, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수 있는 API

developers.naver.com

API 기본 정보에 메서드와 URL을 참고, 요청 변수는 ? & 를 사용해 검색 내용, 출력 건수 등을 설정.

 

요청 예시) 

curl "https://openapi.naver.com/v1/search/shop.xml?query=%EC%A3%BC%EC%8B%9D&display=10&start=1&sort=sim"\

-H "X-Naver-Client-Id: {애플리케이션 등록 시 발급받은 클라이언트 아이디 값}"\
-H "X-Naver-Client-Secret: {애플리케이션 등록 시 발급받은 클라이언트 시크릿 값}" -v

 

 

 

2. ARC에서 요청 코드 복사해 가져오기

RestTemplate rest = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("X-Naver-Client-Id", "네이버-Client-Id");
headers.add("X-Naver-Client-Secret", "네이버-Client-Secret");
String body = "";

HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
ResponseEntity<String> 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);

 

 

3. API 설계

 

4. 네이버 API 검색 결과를 문자열에서 Dto로 변환하기

JSON을 자바에서 다루기 위해서는 JSONObject, JSONArray 클래스가 필요합니다. 이와 같은 클래스들은 org.json 패키지를 설치함으로서 본 프로젝트에 적용할 수 있습니다. https://mvnrepository.com/artifact/org.json/json

 

build.gradle의 dependencies에 아래 내용을 추가하기

// https://mvnrepository.com/artifact/org.json/json
implementation group: 'org.json', name: 'json', version: '20160810'

 

문자열 정보를 JSONObject 로 바꾸기 : 

JSONObject rjson = new JSONObject(result);

JSONObject에서 JSONArray 꺼내기

JSONArray items = rjson.getJSONArray("items");

 

구현 

./src/main/java/com.sparta.w4/utils/NaverShopSearch.java

public class NaverShopSearch {
    public String search(String query) {
        RestTemplate rest = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Naver-Client-Id", "내 Client Id");
        headers.add("X-Naver-Client-Secret", "내 Client Secret Key");
        String body = "";

        HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
        ResponseEntity<String> 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;
    }

    public List<ItemDto> fromJSONtoItems(String result){

        JSONObject rjson = new JSONObject(result);
        JSONArray items = rjson.getJSONArray("items");

        List<ItemDto> itemDtoList = new ArrayList<>();
        for(int i = 0; i < items.length(); i++){
            JSONObject itemJson = items.getJSONObject(i);
            ItemDto itemDto = new ItemDto(itemJson);
            itemDtoList.add(itemDto);
        }

        return itemDtoList;
    }

    public static void main(String[] args) {
        NaverShopSearch naverShopSearch = new NaverShopSearch();
        String result = naverShopSearch.search("아디다스");
    }
}

 

 

5. NaverShopSearch를 컴포넌트로 등록하기

요구조건은 사용자가 검색어를 입력하면 컨트롤러에 전달됩니다. 전달 받은 검색어는 네이버 API에 요청을 하고 그 결과를 사용자에게 응답합니다.

 

이 때 네이버 API를 요청하는 클래스를 컴포넌트로 등록합니다. 

 

 

 

스프링은 자바 기반 프레임워크로서 대규모 어플리케이션을 개발하는데 필요한 여러 기능을 제공합니다. 이러한 기능 중 컴포넌트란 개념이 있습니다. 스프링 컴포넌트란 어플리케이션을 구성하는 요소를 의미합니다. 어플리케이션을 이루는 모듈, 클래스, 인터페이스 등을 의미하며 이러한 컴포넌트들은 스프링의 IoC (Inversion of Control) 컨테이너에 등록되어 관리됩니다.

 

IoC 컨테이너는 컴포넌트를 생성하고 의존성 주입(Dependency Injection)을 통해 다른 컴포넌트와 연결해주는 역할을 합니다. 이를 통해 어플리케이션의 모듈화와 느슨한 결합(Loose Coupling)을 실현합니다. 

 

스프링에서는 다양한 종류의 컴포넌트를 제공합니다. 예를들어, 컨트롤러(Controller), 서비스(Service), 레포지토리(Repository) 등이 있습니다. 각각의 컴포넌트들은 특정한 역할을 수행하며 이를 조합해 어플리케이션을 개발합니다.

 

ㅔ따라서 스프링에서 컴포넌트는 어플리케니션의 구성 요소로서 중요한 역할을 하며 이를 통해 어플리케닝션의 유지 보수서과 확장성을 높일 수 있습니다. 

 

 

컴포넌트 등록이 되면 스프링은 갖다 쓸 수 있는 권한을 갖게 됩니다. 

 

네이버 API는 임의로 컴포넌트 등록을 해야 합니다..

./src/main/java/com.sparta.w4/utils/NaverShopSearch.java

@Component
public class NaverShopSearch {
    public String search(String query) {
        RestTemplate rest = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Naver-Client-Id", "내 Client Id");
        headers.add("X-Naver-Client-Secret", "내 Client Secret Key");
        String body = "";

        HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
        ResponseEntity<String> 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;
    }

    public List<ItemDto> fromJSONtoItems(String result){

        JSONObject rjson = new JSONObject(result);
        JSONArray items = rjson.getJSONArray("items");

        List<ItemDto> itemDtoList = new ArrayList<>();
        for(int i = 0; i < items.length(); i++){
            JSONObject itemJson = items.getJSONObject(i);
            ItemDto itemDto = new ItemDto(itemJson);
            itemDtoList.add(itemDto);
        }

        return itemDtoList;
    }
}

./src/main/java/com.sparta.w4/utils/NaverShopSearch.java

 

컴포넌트로 등록된 클래스는 컨트롤러에서 private final 로 선언해서 사용합니다. 

 

@Component 어노테이션을 이용하면 Bean Configuration 파일에 Bean을 등록하지 않아도 사용할 수 있습니다.

즉, Bean 등록을 Bean 클래스에 할 수 잇음을 의미합니다. 

 

@Component 어노테이션은 기본적으로 타입 기반 자동 주입 어노테이션입니다. @Autowired, @Resource와 비슷한 기능을 수행한다고 할 수 있습니다. 

 

./src/main/java/com.sparta.w4/controller/SearchRequestController.java

@RequiredArgsConstructor
@RestController
public class SearchRequestController {

    private final NaverShopSearch naverShopSearch;

    @GetMapping("/api/search")
    public List<ItemDto> getItems(@RequestParam String query){
        String resultString = naverShopSearch.search(query);
        return naverShopSearch.fromJSONtoItems(resultString);
    }

}

@RequestPram 어노테이션은 HttpServletRequest 객체와 같은 역할을 합니다. 

 

HttpServletRequest에서는 getParameter() 메소드를 이용하지만 

@RequestParam을 이용해 받아오는 방법이 있습니다.

 

메소드의 파라미터 값에 @RequestParam을 넣어줍니다. 

@RequestPram("가져올 데이터의 이름") [데이터 타입] [변수이름]

만약 parameter 값을 넘겨 주지 않으면 400 에러가 발생합니다.

 

6. 데이터의 흐름 

 

검색창 입력값 가져오기

검색창 입력값 검사하고 입력하지 않은 경우에는 focus

GET /api/search?query=${query} 요청하기

for 문마다 itemDto꺼내 HTML을 만들고 검색결과를 목록에 붙이기

addHTML 완성하기

 

 

7. 주의

json 객체를 다른 함수에 전달해주기 -> 문자열로 변환해 전달 : JSON.stringfy(responseDto)

전달받은 문자열이 만약 json 형태인 경우 -> JSON 객체로 전달받음 

 

8. 스케줄러

특정 시간에 관심상품 목록에서 제목을 바탕으로 검색을 해고, 최저가 정보가 업데이트 되도록함

 

구현 : 매 한 시마다 동작 

@Scheduled(cron = "0 0 1 * * *")
    public void updatePrice() throws InterruptedException {
        System.out.println("가격 업데이트 실행");
        // 저장된 모든 관심상품을 조회합니다.
        List<Product> productList = productRepository.findAll();
        for (int i=0; i<productList.size(); i++) {
            // 1초에 한 상품 씩 조회합니다 (Naver 제한)
            TimeUnit.SECONDS.sleep(1);
            // i 번째 관심 상품을 꺼냅니다.
            Product p = productList.get(i);
            // i 번째 관심 상품의 제목으로 검색을 실행합니다.
            String title = p.getTitle();
            String resultString = naverShopSearch.search(title);
            // i 번째 관심 상품의 검색 결과 목록 중에서 첫 번째 결과를 꺼냅니다.
            List<ItemDto> itemDtoList = naverShopSearch.fromJSONtoItems(resultString);
            ItemDto itemDto = itemDtoList.get(0);
            // i 번째 관심 상품 정보를 업데이트합니다.
            Long id = p.getId();
            productService.updateBySearch(id, itemDto);
        }
    }

 

또한 

 

Application 클래스에 @EnableScheduling 어노테이션을 추가해서 스케줄러를 작동합니다. 

 

 

9. MYSQL 연동 

mysql 연동하기 -> AWS RDS 데이터베이스 생성 :

엔진유형 : MYSQL,

템플릿 : 프리 티어,

설정 : DB 인스턴스 식별자 입력

마스터 사용자, 암호 입력

연결-추가연결구성 : 퍼블릭 액세스 가능을 예로 설정

VPC 새로생성 : 새 보안 그룹 이름 입력

가용영역 설정

데이ㅓ베이스 포트 : 3306

추가구성 : 초기 데이터베이스 이름입력

 

-> 연결 & 보안 :

 보안 : VPC 보안 그룹 : 보안그룹 아이딕 클릭 후 인바운드 규칙 편집 : 포트범위 3306, 위치무관으로 설정

 

-> 연결 & 보안 :

엔드포인트 복사 : ex) [데이터베이스 이름].[문자열].ap-northeast-2.rds.amazonaws.com (mysql접근)

 

-> IntelliJ 데이터베이스 탭 : DataSource : MySQL 클릭 

Host : 복사한 엔드포인트

User : admin

password : 비밀번호

Database : 추가구성에 명시한 초기 데이터베이스 이름 입력

 

-> 접속 불량 시 문제 

- 포트 닫힘 : 3306

- 아이디, 비밀번호 문제 -> 아이디 비밀번호 모를 경우 해당 디비 삭제

 

-> 스프링 설정 : application.properties

spring.datasource.url=jdbc:mysql://나의엔드포인트:3306/myselectshop
spring.datasource.username=나의USERNAME
spring.datasource.password=나의패스워드
spring.jpa.hibernate.ddl-auto=update

 

 

10. OG 태그 작업

<meta property="og:title" content="00만의 셀렉샵">
<meta property="og:description" content="관심상품을 선택하고, 최저가 알림을 확인해보세요!">
<meta property="og:image" content="images/og_selectshop.png">

 

 

11. EC2 구매

Ubuntu Server 18.04 LTS로 구매

키 페어 설정 

EC2 접속하기 : SSH, 22번 포트가 열려 있음(인바운드 규칙 참고)

 

AWS EC2 접속 : 

ssh -i 키페어 ubuntu@AWS아이피
exit // 접속 종료

 

 

12. 빌드 & 배포

Gradle 탭 클릭 -> build 더블클릭 -> build 더블 클릭 : build 폴더>libs>...SNAPSHOT.jar (빌드파일)

 

Open JDK 설치 : 

ssh -i 키페어 ubuntu@AWS아이피 // EC2 접속
sudo apt-get update
sudo apt-get install openjdk-8-jdk
java -version // 자바 설치 확인

 

Filezilla로 배포 파일 업로드 : 

Filezilla에서 EC2 접속 : SFTP, 아이피, 포트(22), 로그온 유형(키파일), 사용자이름(ubuntu), 키페어파일열기 : 연결

: jar 파일 업로드 

: 실행하기

java -jar 파일이름.jar

: EC2 인바운드 규칙 : 80번, 8080번 열어주기

 

 

13. 포트포워딩, nohup

포트포워딩 : 

sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

nohup :

nohup java -jar 파일이름.jar & //실행

ps -ef | grep java
kill -9 [숫자] // 종료