의약품의 이름 정보를 쿼리에 담아 요청을 보내면 해당 하는 알약의 정보를 반환 받는 기존의 로직을
수강한 강의 영상을 바탕으로 개선한 과정 기록 

기존 로직

  • controller에 비지니스 로직이 섞여 있는 모습 
package _PF026.DrDrug.controller;

import _PF026.DrDrug.dto.MedicationDto;
import _PF026.DrDrug.dto.ResponseDto;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

@RestController
public class DrugOpenAPIController {
    private final static String BASE_URL = "http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList";
    private final String API_KEY = "your key"
		
		@GetMapping(value = "/MedicationDto/{itemName}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<MedicationDto> getDrugInfo(@PathVariable String itemName){
        DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
        String encodedItemName = URLEncoder.encode(itemName, StandardCharsets.UTF_8);
        WebClient webClient = WebClient.builder()
                .uriBuilderFactory(factory)
                .baseUrl(BASE_URL)
                .build();

        Mono<ResponseDto> response = webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .queryParam("serviceKey", API_KEY)
                        .queryParam("pageNo", "1")
                        .queryParam("numOfRows", "1")
                        .queryParam("entpName", "")
                        .queryParam("itemName", encodedItemName)
                        .queryParam("itemSeq", "")
                        .queryParam("efcyQesitm", "")
                        .queryParam("useMethodQesitm", "")
                        .queryParam("atpnWarnQesitm", "")
                        .queryParam("atpnQesitm", "")
                        .queryParam("intrcQesitm", "")
                        .queryParam("seQesitm", "")
                        .queryParam("depositMethodQesitm", "")
                        .queryParam("openDe", "")
												.queryParam("updateDe", "")
                        .queryParam("type", "json")
                        .build())
                .retrieve()
                .bodyToMono(ResponseDto.class);

        return response.flatMap(this::convertToMedication);
    }
    private Mono<MedicationDto> convertToMedication(ResponseDto responseDTO) {
        List<MedicationDto> medicationDtos = responseDTO.getBody().getItems();
        if (medicationDtos != null && !medicationDtos.isEmpty()) {
            return Mono.just(medicationDtos.get(0));
        } else {
            return Mono.empty();
        }
    }
}

Rest API 방식으로 url/MedicationDto/{itemName} 으로 요청을 보내면

itemName에 해당 하는 의약품 정보를 공공포털 api를 이용하여 get 요청을 보내 의약품에 대한 정보를 얻는다.

얻은 정보를 전용 Dto 객체로 파싱하여 저장하고 다른 계층에 적합한 Dto 객체로 다시 변환하여 전송한다.

 

1차 개선

  • SOLID 원칙에서(S) Single Principle Responsibility 원칙에 따라서
  • 한 클래스는 하나의 책임을 가져야 한다.
  • 공공포털 조회 기능을 service 로직으로 분리
package _PF026.DrDrug.service;

import _PF026.DrDrug.dto.MedicationDto;
import _PF026.DrDrug.dto.ResponseDto;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Service
public class DrugOpenAPIService {
    private final static String BASE_URL = "http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList";
    private final String API_KEY = "your api key";

    public Mono<MedicationDto> getDrugInfo(String itemName){
        DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
        String encodedItemName = URLEncoder.encode(itemName, StandardCharsets.UTF_8);
        WebClient webClient = WebClient.builder()
                .uriBuilderFactory(factory)
                .baseUrl(BASE_URL)
                .build();

        Mono<ResponseDto> response = webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .queryParam("serviceKey", API_KEY)
                        .queryParam("pageNo", "1")
                        .queryParam("numOfRows", "1")
                        .queryParam("entpName", "")
                        .queryParam("itemName", encodedItemName)
                        .queryParam("itemSeq", "")
                        .queryParam("efcyQesitm", "")
                        .queryParam("useMethodQesitm", "")
                        .queryParam("atpnWarnQesitm", "")
                        .queryParam("atpnQesitm", "")
                        .queryParam("intrcQesitm", "")
                        .queryParam("seQesitm", "")
                        .queryParam("depositMethodQesitm", "")
                        .queryParam("openDe", "")
                        .queryParam("updateDe", "")
                        .queryParam("type", "json")
                        .build())
                .retrieve()
                .bodyToMono(ResponseDto.class);

        return response.flatMap(this::convertToMedication);
    }

    private Mono<MedicationDto> convertToMedication(ResponseDto responseDTO) {
        List<MedicationDto> medicationDtos = responseDTO.getBody().getItems();
        if (medicationDtos != null && !medicationDtos.isEmpty()) {
            return Mono.just(medicationDtos.get(0));
        } else {
            return Mono.empty();
        }
    }
}
  • 컨트롤러 코드 수정
package _PF026.DrDrug.controller;

import _PF026.DrDrug.dto.MedicationDto;
import _PF026.DrDrug.service.DrugOpenAPIService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class DrugOpenAPIController {
    private final DrugOpenAPIService drugOpenAPIService;

    @Autowired
    public DrugOpenAPIController(DrugOpenAPIService drugOpenAPIService) {
        this.drugOpenAPIService = drugOpenAPIService;
    }

    @GetMapping(value = "/MedicationDto/{itemName}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<MedicationDto> getDrugInfo(@PathVariable String itemName){
        return drugOpenAPIService.getDrugInfo(itemName);
    }
}

 

2차 개선

  • SOLID 원칙에서(D) Dependency Inversion Principle 원칙에 따라서 다시 수정
  • 구현체가 아닌 인터페이스에 의존함으로써 결합력을 낮추고 응집력을 높힐 수 가 있다.
  • 변경된 컨트롤러
package _PF026.DrDrug.controller;

import _PF026.DrDrug.dto.MedicationDto;
import _PF026.DrDrug.service.DrugService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class DrugOpenAPIController {
    private final PublicApiService publicApiService;

    @Autowired
    public DrugOpenAPIController(DrugService drugService) {
        this.publicApiService = drugService;
    }

    @GetMapping(value = "/MedicationDto/{itemName}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<Object> getDrugInfo(@PathVariable String itemName){
        return publicApiService.getDrugInfo(itemName);
    }
}
  • 추가되고 변경된 service 로직
package _PF026.DrDrug.service;

import reactor.core.publisher.Mono;

public interface PublicApiService {
    Mono<Object> getInfo(String query);
}
package _PF026.DrDrug.service;

import _PF026.DrDrug.dto.MedicationDto;
import _PF026.DrDrug.dto.ResponseDto;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Service
public class DrugOpenAPIService implements PublicApiService {
    private final static String BASE_URL = "http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList";
    private final String API_KEY = "your api key";

    public Mono<Object> getDrugInfo(String itemName){
        DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
        String encodedItemName = URLEncoder.encode(itemName, StandardCharsets.UTF_8);
        WebClient webClient = WebClient.builder()
                .uriBuilderFactory(factory)
                .baseUrl(BASE_URL)
                .build();

        Mono<ResponseDto> response = webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .queryParam("serviceKey", API_KEY)
                        .queryParam("pageNo", "1")
                        .queryParam("numOfRows", "1")
                        .queryParam("entpName", "")
                        .queryParam("itemName", encodedItemName)
                        .queryParam("itemSeq", "")
                        .queryParam("efcyQesitm", "")
                        .queryParam("useMethodQesitm", "")
                        .queryParam("atpnWarnQesitm", "")
                        .queryParam("atpnQesitm", "")
                        .queryParam("intrcQesitm", "")
                        .queryParam("seQesitm", "")
                        .queryParam("depositMethodQesitm", "")
                        .queryParam("openDe", "")
                        .queryParam("updateDe", "")
                        .queryParam("type", "json")
                        .build())
                .retrieve()
                .bodyToMono(ResponseDto.class);

        return response.flatMap(this::convertToMedication);
    }

    private Mono<MedicationDto> convertToMedication(ResponseDto responseDTO) {
        List<MedicationDto> medicationDtos = responseDTO.getBody().getItems();
        if (medicationDtos != null && !medicationDtos.isEmpty()) {
            return Mono.just(medicationDtos.get(0));
        } else {
            return Mono.empty();
        }
    }
}

 

3차 개선

  • Lombok 라이브러리를 사용하여 controller 코드 간결화
  • @RequiredArgsConstructor를 사용하여 빈으로 등록된 클래스를 이용해 의존성 주입
    • 이 어노테이션은 클래스 내부에 final 또는 @NonNull이 붙은 필드에 대해 생성자를 자동으로 생성 
    • 생성자가 명시적으로 존재하지 않아도, Lombok 라이브러리가 컴파일 시점에 생성자를 추가해준다.
    • 따라서 @Autowired 어노테이션이 없어도 스프링이 자동으로 의존성 주입을 해준다.
@RestController
@RequiredArgsConstructor
public class DrugOpenAPIController {
    private final PublicApiService drugOpenAPIService;

    @GetMapping(value = "/MedicationDto/{itemName}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<Object> getDrugInfo(@PathVariable String itemName){
        return drugOpenAPIService.getInfo(itemName);
    }
}
  • 해당 코드만 봐서는 구현체가 어떤 클래스로 주입받았는지 생략이 되어 판별하기가 쉽지않다, 그 판별 원리는 다음과 같다.
    • 우선적으로 스프링 컨테이너가 해당 타입의 빈을 찾는다. (위에서는 PublicApiService 타입)
    • 해당 타입의 빈이 하나만 있다면 그 빈을 주입한다.
    • 여러개의 빈이 있다면 빈의 이름(메소드 이름, @Bean 어노테이션의 이름 속성)을 사용하여 빈을 선택 
    • 만약 일치하는 이름의 빈이 없다면 NoUniqueBeanDefinitionException 발생
  • @Primary 어노테이션을 사용하면, 동일한 타입의 빈이 여러 개 있을 때 기본적으로 선택되는 빈을 지정할 수 있다.
  • @Qualifier 어노테이션을 사용하면, 동일한 타입의 빈이 여러 개 있을 때 주입될 빈을 명시적으로 지정할 수 있다.

 

코드 관련 링크

https://suhanlim.tistory.com/222

https://suhanlim.tistory.com/110

도움을 받은 강의 링크

좋은 지식 공유해주셔서 감사합니다 김영환 멘토님

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

POJO (Plain Old Java Object)

POJO는 단순히 데이터를 나타내는 클래스이며 특정한 인터페이스를 구현하거나, 특정한 클래스를 상속받지 않는 단순한 클래스를 의미한다. 주로 필드, 메소드, 생성자 등을 포함할 수 있으며, 비즈니스 로직 처리 보다는 데이터를 저장하고 전달하는데 사용된다.
@Data
public class Medication {
    private String entpName;
    private String itemName;
    private String efcyQesitm;
    private String useMethodQesitm;
    private String atpnWarnQesitm;
    private String atpnQesitm;
    private String intrcQesitm;
    private String seQesitm;
    private String depositMethodQesitm;
    private String itemImage;
}

DTO (Data Transfer Object)

데이터 전송 객체(Data Transfer Object, DTO)는 계층간 데이터 교환을 위한 자바 Beans 이다.
DTO도 POJO의 한 형태로, 시스템 간 또는 계층 간의 데이터 전송을 위해 사용된다. 보통 DTO는 각 필드에 대한 getter/setter 메소드를 포함한다. DTO는 보통 계층 간의 데이터 전송을 목적으로 사용되며, 각 필드를 캡슐화하여 사용하는 POJO와 다소 차이가 있다.
여기서 계층이란 Controller, Service, DAO 등을 의미한다.
class Item {
    @JsonProperty("entpName")
    private String entpName;
    @JsonProperty("itemName")
    private String itemName;
    @JsonProperty("efcyQesitm")
    private String efcyQesitm;
    @JsonProperty("useMethodQesitm")
    private String useMethodQesitm;
    @JsonProperty("atpnWarnQesitm")
    private String atpnWarnQesitm;
    @JsonProperty("atpnQesitm")
    private String atpnQesitm;
    @JsonProperty("intrcQesitm")
    private String intrcQesitm;
    @JsonProperty("seQesitm")
    private String seQesitm;
    @JsonProperty("epositMethodQesitm")
    private String depositMethodQesitm;
    @JsonProperty("itemImage")
    private String itemImage;

    // getter and setter methods
    // ...
}
class Header {
    @JsonProperty("resultCode")
    private String resultCode;
    @JsonProperty("resultMsg")
    private String resultMsg;

    // getter and setter methods
    // ...
}
class Body {
    @JsonProperty("items")
    private List<Item> items;

}

public class ResponseDTO {
    @JsonProperty("header")
    private Header header;
    @JsonProperty("body")
    private Body body;

    // getter and setter methods
    // ...
}

 

+ DAO (Data Access Object)

데이터베이스에 접근하는 코드를 캡슐화하기 위한 설계 패턴

1. Spring-Boot JAR 파일로 내보내기

자세한 설명은 해당 링크 참조: https://suhanlim.tistory.com/192

만약 GitHub에 있는 코드 그대로 사용시 해당 오류가 발생할 수 있는데 프로그램의 단위테스트 자체가 실패했다는 의미이므로 DB 설정 부분을 지우거나 기존 실습 했던 값으로 바꿔야 한다.

드디어 대망의 jar 파일 생성완료

2. EC2 인바운드 규칙 추가

3. FileZilla를 사용하여 jar 파일을 EC2 내부로 이동

FileZilla 대신 git을 사용하여 작업하여도 된다.

https://filezilla-project.org/download.php?platform=win64

도움을 받은 글: https://app-developer.tistory.com/93

더블 클릭으로 파일 전송

4. EC2에 Java 설치 및 jar 파일 실행

파일 전송 확인

sudo yum install java-17-amazon-corretto-devel

java17 설치 확인 후 chmod +x 파일명.jar 로 실행 권한 부여

java -jar 파일명.jar --spring.datasource.url=jdbc:mysql://엔드포인트:3306/데이터베이스 이름 --spring.datasource.username=유저명 --spring.datasource.password=비밀번호

프로젝트 실행 완료

5. 테스트

http://EC2 IP:8080/medications

6. 안드로이드 프로그램 url 주소 수정

배포 성공!

1. EC2 실행

(인스턴스 실행)

2. Puttygen, Putty 다운로드 후 Puttygen 실행

https://www.puttygen.com/download-putty

https://www.puttygen.com/

EC2 생성시 만들었던 .pem 파일 선택

Save private key 클릭 후 파일명 설정시 파일명.ppk 파일 생성

3. Putty 실행

Browse 버튼을 통해 아까 생성한 .ppk 파일 선택

EC2 IP주소 입력 후 Open

Accept 클릭

ec2-user 입력시 접속이 되면 성공

3. EC2에 MySQL 설치

https://dev.classmethod.jp/articles/ec2-mysql-install-confilicting-requests-error-kr/
sudo dnf install https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
sudo dnf install mysql-community-server

해당 명령어 입력시 MySQL 설치 중간 중간 Y/N 을 물을 때 Y입력 

mysql —version 으로 설치가 잘 됬는지 확인

4. EC2를 통해 RDS 접속 후 사용할 DB생성

mysql -u {마스터 사용자 이름} -p -h {RDS 인스턴스 엔드포인트}
// 이후 패스워드 입력

4-2. RDS 데이터베이스 생성, 조회 SQL

CREATE database Medications default character set utf8mb4 collate utf8mb4_unicode_ci;

USE Medications;

CREATE TABLE Medications (
ID INT PRIMARY KEY,
medicationId VARCHAR(255),
NAME VARCHAR(255)
);
insert into Medications (ID,medicationId,NAME)
values  (3, 'e12','감기약');
insert into Medications (ID,medicationId,NAME)
values  (2, 'z12','타이레놀');
insert into Medications (ID,medicationId,NAME)
values  (1, 's12','수면제');
SELECT * FROM Medications;

exit명령어로 mysql 빠져 나오기

선행 요소: AWS 회원가입

1. AWS EC2 시작

주의!: 과금 요소가 생길 수 있으니 최대한 프리티어로 생성해야 한다.

키 페어 생성시 RestAPI.pem 파일이 다운로드 되는데 이는 해당 EC2를 실행하는데 꼭 필요한 보안 파일 이기 때문에 삭제하지 말고 중요한 곳에 모셔두어야 한다.

그 외 설정은 추가적으로 건드는 것 없이 인스턴스 시작

2. 탄력적 IP 할당

지금 실행 시킨 EC2는 새로 연결시 url이 바뀌기 때문에 이를 방지하기 위해서는 탄력적 IP를 할당하여 사용할 필요가 있다.

3. RDS 생성

가장 중요!!!: 반드시 RDS는 프리 티어로 만들어야 한다. 프로덕션, 개발/테스트 용도로 만들 경우 세팅도 프리티어와 다를 뿐 아니라 아무런 이용이 없다 해도 한 달 고정 비용이 많이 발생하기 때문

해당 비밀번호를 반드시 기억해야 한다.

이후 따로 설정을 건들지 않고 데이터 베이스 생성 버튼을 클릭한다.

4. EC2, 데이터베이스 연결

작성 이유:

프로젝트를 처음 진행하면서 데이터 파이프라인 구축에 애를 먹었던 경험을 기록으로 남겨 쉽게 따라 할 수 있도록 방법을 공유하자는 취지로 글을 쓰게 됬다.

목표:

서버와 통신하여 데이터 베이스에 있는 정보를 읽어서 안드로이드 프로그램 화면 위에 띄우는 데이터 파이프라인 생성 

  1. 본인의 컴퓨터 안에서 localhost 내부에서 응답 처리
  2. AWS,RDS로 이전하여 작업 환경 구축

사용기술:

Android, Spring Boot, Gradle, JPA ORM, Mysql

전체 소스 코드:

https://github.com/suhanlim/RestAPI

1. Mysql

https://suhanlim.tistory.com/213

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (Mysql) -1(완) 설치부터 스키마 생성까지

1. MySQL 다운로드 설치 중요!: 여기서 설정해둔 비밀번호는 따로 메모해두거나 꼭 암기해 두어야 한다. 2. MySQL Workbench 실행 후 연결 객체 생성 +버튼을 통해 새 연결 객체 생성 여기서 Username과 설

suhanlim.tistory.com

2. Spring Boot

https://suhanlim.tistory.com/214

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (SpringBoot)-1 프로젝트 생성 및 클래스 작성

1. 프로젝트 생성 https://start.spring.io/ 2. 생성된 zip 파일 위치 설정 및 실행 로컬 디스크(c)로 이동 후 압축 해제 IntelliJ로 실행 3. controller, domain, repository로 분할 클래스,인터페이스 작성 MedicationsCont

suhanlim.tistory.com

https://suhanlim.tistory.com/215

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (SpringBoot)-2(완) Mysql DB설정 (IntelliJ)

1. application.properties.yml 내용 추가 spring.datasource.url=jdbc:mysql://localhost:3306/Medications spring.datasource.username=testuser spring.datasource.password="스키마 생성시 설정한 비밀번호" spring.jpa.hibernate.ddl-auto=update 2. DB

suhanlim.tistory.com

3. Android

https://suhanlim.tistory.com/211

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (Android)-1 프론트 UI 작업

1. Empty Activity 프로젝트 생성 2. Device Manager 추가 저는 미리 Create device 버튼을 통해 생성한 상태라 Pixel 4 API 33이 이미 존재하는 상태입니다. 3. app/res/layout/activity_main.xml 작업 4. 테스트 프로그램 실

suhanlim.tistory.com

https://suhanlim.tistory.com/212

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (Android)-2(완) Rest API 통신 준비

1. 의존성 추가 Rest Api를 사용하는데 있어 다양한 라이브러리가 존재하지만 여기서는 가장 속도가 빠른 Retrofit를 사용할 계획이다. https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson https://mv

suhanlim.tistory.com

 

4. AWS, RDS 배포

https://suhanlim.tistory.com/217

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (AWS-EC2, RDS) -1 인스턴스 생성

선행 요소: AWS 회원가입 1. AWS EC2 시작 주의!: 과금 요소가 생길 수 있으니 최대한 프리티어로 생성해야 한다. 키 페어 생성시 RestAPI.pem 파일이 다운로드 되는데 이는 해당 EC2를 실행하는데 꼭 필

suhanlim.tistory.com

https://suhanlim.tistory.com/218

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (AWS-EC2, RDS) -2 EC2 접속 및 DB 생성

1. EC2 실행 (인스턴스 실행) 2. Puttygen, Putty 다운로드 후 Puttygen 실행 https://www.puttygen.com/download-putty https://www.puttygen.com/ EC2 생성시 만들었던 .pem 파일 선택 Save private key 클릭 후 파일명 설정시 파일

suhanlim.tistory.com

https://suhanlim.tistory.com/219

 

Android, Spring Boot, Mysql 데이터 파이프 라인 구축 실습 (AWS-EC2, RDS) -3(완) JAR 파일 생성 및 실행

1. Spring-Boot JAR 파일로 내보내기 자세한 설명은 해당 링크 참조: https://suhanlim.tistory.com/192 만약 GitHub에 있는 코드 그대로 사용시 해당 오류가 발생할 수 있는데 프로그램의 단위테스트 자체가 실

suhanlim.tistory.com

 

1. application.properties.yml 내용 추가

spring.datasource.url=jdbc:mysql://localhost:3306/Medications
spring.datasource.username=testuser
spring.datasource.password="스키마 생성시 설정한 비밀번호"
spring.jpa.hibernate.ddl-auto=update

2. DB에 삽입할 테스트 sql 문 data.sql

insert into medications.medications (ID,medication_Id,NAME)
values  (2, 'e12','감기약');
insert into medications.medications (ID,medication_Id,NAME)
values  (3, 'z12','타이레놀');
insert into medications.medications (ID,medication_Id,NAME)
values  (4, 's12','수면제');

3. DB 연결

아이디, 비밀번호, DB명 입력

Insert 작업이 완료 되었나 select 문을 통해 MySQL Workbench에서 확인

USE Medications;
SELECT * FROM Medications;

4. 추가 테스트

http://localhost:8080/medications

https://chrome.google.com/webstore/detail/json-viewer/gbmdgpbipfallnflgajpaliibnhdgobh?hl=ko

JSON Viewer를 쓴 모습

API Tester 확장 플러그인을 사용하여 Create 작업이 잘 되는지 확인 다른 API 테스트 툴이 있다면 그걸 사용해도 무방하다.

https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=ko

 

1. 프로젝트 생성

https://start.spring.io/

2. 생성된 zip 파일 위치 설정 및 실행

로컬 디스크(c)로 이동 후 압축 해제

IntelliJ로 실행

3. controller, domain, repository로 분할 클래스,인터페이스 작성

MedicationsController.java

package _PF026.DrDrug.controller;

import _PF026.DrDrug.domain.Medications;
import _PF026.DrDrug.repository.MedicationsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
public class MedicationsController {
    @Autowired
    private MedicationsRepository repository;

    @GetMapping("/medications")
    public List<Medications> getAllMedications(){
        return repository.findAll();
    }

    @GetMapping("/medications/{id}")
    public Medications getMedicationsDetail(@PathVariable long id){
        Optional<Medications> medications = repository.findById(id);
        if(medications.isEmpty()){
            throw new RuntimeException("Course not found with id "+id);
        }
        return medications.get();
    }
    @PostMapping("/medications")
    public void createMedications(@RequestBody Medications medications){
        repository.save(medications);
    }

    // 객체가 존재하면 업데이트 없다면 삽입
    @PutMapping("/medications")
    public void updateMedications(@RequestBody Medications medications){
        repository.save(medications);
    }

    // 객체가 존재하면 업데이트 없다면 삽입
    @DeleteMapping("/medications/{id}")
    public void deleteMedications(@PathVariable long id){
        repository.deleteById(id);
    }

}

Medications.java

package _PF026.DrDrug.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter @Setter
public class Medications {
    @Id @GeneratedValue
    private Long id;
    private String medicationId;
    private String name;
}

MedicationsRepository.java

package _PF026.DrDrug.repository;

import _PF026.DrDrug.domain.Medications;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MedicationsRepository extends JpaRepository<Medications,Long> {

}

<Repository>를 만들때는 EntityManager와 JpaRepository 두 가지 다른 방법이 존재하는데 여기서는 간단한 CRUD연산만 필요 하므로 JpaRepository를 사용하였다.

  1. EntityManager를 사용: EntityManager는 JPA의 핵심 컴포넌트로, 엔티티를 관리하며 데이터베이스와의 모든 상호작용을 처리합니다. EntityManager를 사용하면 직접적으로 데이터베이스 작업을 제어할 수 있습니다. 복잡하고 세밀한 작업이 필요한 상황에서는 EntityManager를 사용하는 것이 좋을 수 있습니다. 하지만 EntityManager를 사용하면 코드가 복잡해질 수 있고, JPA를 깊이 이해해야 할 수 있습니다.
  2. JpaRepository를 상속: JpaRepository를 상속받는 Repository를 사용하면 CRUD 연산을 매우 쉽게 처리할 수 있습니다. JpaRepository는 기본적인 CRUD 메서드를 제공하며, 이를 상속받는 클래스는 이 메서드를 직접 사용할 수 있습니다. 또한, 메서드 이름으로 쿼리를 생성하는 기능을 제공하여 데이터베이스 작업을 매우 쉽게 만들어줍니다. JpaRepository를 사용하면 보일러플레이트 코드를 크게 줄일 수 있지만, 복잡한 쿼리나 특정 데이터베이스 작업을 처리하기 어려울 수 있습니다.

 

+ Recent posts