[LIKELION] 221102

고관운·2022년 11월 2일

회고

😄 느낀점

  • docker를 배우면서 docker가 나의 강점이 될 수 있다는 것을 알았다. docker의 오류를 많이 겪으면서 각 오류별 해결 방법에 대해 공부해보면 좋을 것 같다.

😁 목표

  • 프로그래머스 알고리즘

알고리즘

소수찾기(프로그래머스)

문제

👀 문제 확인하기 👀

에라토스테네스의 체

  1. 리스트 이용
    public int solution(int N) {
        ArrayList<Integer> arr = new ArrayList<>();

        for (int i = 2; i <= N; i++) {
            arr.add(i);
        }
        
        for (int i = 2; i * i <= N; i++) {
            for (int j = 0; j < arr.size(); j++) {
                if(arr.get(j) % i == 0 && arr.get(j) > i) {
                    arr.remove(j);
                }
            }
        }

        return arr.size();
    }

🔴 속도 이슈 발생 : 리스트의 제거 방식(remove)이 제거할 원소를 제거하여 복사한 후, 기존 리스트를 제거하기 때문에 시간이 오래 걸림

👉 속도 개선 방법 : 다른 배열을 만들어서 그 배열에서 소수 여부 판단

import java.util.ArrayList;

public class SieveOfEratosthenes {
    // 소수여부를 판단해주는 배열을 따로 생성하여 구현
    public int solution(int N) {
        // 2~N까지 리스트에 넣기
        boolean[] checks = new boolean[N+1];

        checks[0] = false;
        checks[1] = false;

        for (int i = 2; i < N+1; i++) {
            checks[i] = true;
        }

        // 2부터 루트 N까지 나누며 배수 제거
        for (int i = 2; i * i < N+1; i++) {
            for (int j = i*i; j < N+1; j+=i) {
                if(j % i == 0) {
                    checks[j] = false;
                }
            }
        }

        // 소수 리스트를 따로 생성
        ArrayList<Integer> primeNum = new ArrayList<>();
        for (int i = 0; i < N+1; i++) {
            if(checks[i]){
                primeNum.add(i);
            }
        }

        return primeNum.size();
    }
}

Spring Boot

데이터 DB에 Insert

방법

  1. Test코드에서 넣을 수 있음
    ➡ 하지만 빌드할때마다 돌아가므로 좋지 않음(매번 11만 건씩 DB에 들어감)
    ➡ 파일명 Dependency(경로가 특정 컴퓨터에서만 돌어감)
  2. Service로 따로 만듬

Service 생성

Presentation → Service(Business) → Data

  • Presentation과 비즈니스 로직을 분리하기 위한 계층(간단한 예제를 만들때는 필요가 x)
  • @Service로 설정
    @Component 어노테이션과 같은 기능
    @ComponentScan 할때 Bean으로 등록됨

🔴 속도 문제 개선

  • Service에 트랜잭션을 넣어줌(@Transactional)
  • Stream까지 넣어서 병렬 처리
import com.springboot.springbootcoreguide.dao.HospitalDao;
import com.springboot.springbootcoreguide.domain.Hospital;
import com.springboot.springbootcoreguide.parser.ReadLineContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.List;
import java.util.Optional;

@Service
public class HospitalService {

    private final ReadLineContext<Hospital> hospitalReadLineContext;
    private final HospitalDao hospitalDao;

    public HospitalService(ReadLineContext<Hospital> hospitalReadLineContext, HospitalDao hospitalDao) {
        this.hospitalReadLineContext = hospitalReadLineContext;
        this.hospitalDao = hospitalDao;
    }

    @Transactional
    public int insertLargeVolumeHospitalData(String filename) {
        List<Hospital> hospitalList;
        try {
            hospitalList = hospitalReadLineContext.readByLine(filename);
            System.out.println("파싱이 끝났습니다.");
            hospitalList.stream()
                    .parallel()
                    .forEach(hospital -> {
                        try {
                            this.hospitalDao.add(hospital); // db에 insert하는 구간
                        } catch (Exception e) {
                            System.out.printf("id:%d 레코드에 문제가 있습니다.\n",hospital.getId());
                            throw new RuntimeException(e);
                        }
                    });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if (!Optional.of(hospitalList).isEmpty()) {
            return hospitalList.size();
        } else {
            return 0;
        }
    }
}

🔴 하위 코드 HospitalParserTest에 추가

    @Test
    @DisplayName("DB에 Hospital 데이터 넣기")
    void insertData() throws IOException {
        hospitalDao.deleteAll();
        String filename = "D:\\고관운 자료\\멋쟁이사자처럼\\수업 자료\\fulldata_01_01_02_P_의원_utf8.csv";
        int cnt = this.hospitalService.insertLargeVolumeHospitalData(filename);

        assertEquals(cnt, hospitalDao.getCount());
        System.out.println("DB에 들어간 데이터 개수 : " + cnt);
    }

HospitalController

  • add : 데이터 추가
  • selectById : 해당 id 데이터 가져오기(없을 경우 예외처리까지)

🟢 Optional<> : Null을 줄이는 추세(Null 대신)

  • 비었는지 check
  • Java8 스타일 기능과 연계

사용방법
Optional<Hospital> opt = Optional.of(hospital);
if(!opt.isEmpty())

import com.springboot.springbootcoreguide.dao.HospitalDao;
import com.springboot.springbootcoreguide.domain.Hospital;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@RestController
@RequestMapping("/api/v1/hospital")
@Slf4j
public class HospitalController {
    private final HospitalDao hospitalDao;

    public HospitalController(HospitalDao hospitalDao) {
        this.hospitalDao = hospitalDao;
    }

    @GetMapping("/{id}")
    public ResponseEntity<Hospital> selectById(@PathVariable int id) {
        Hospital selectedHospital = hospitalDao.findById(id);
        Optional<Hospital> opt =  Optional.of(selectedHospital);

        if(!opt.isEmpty()){
            return ResponseEntity.ok().body(selectedHospital);
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new Hospital());
        }
    }

    @PostMapping("")
    public ResponseEntity<Integer> add(@RequestBody Hospital hospital) {
        log.info("정보를 등록했습니다.");
        hospitalDao.add(hospital);
        return ResponseEntity
                .ok()
                .body(hospitalDao.add(hospital));
    }

}

Docker 빌드 및 SpringBoot 실행

실습

  1. maven의 package 만들기(Docker에서 실행하기 전 .jar가 생성되는지 테스트)
    👉 maven의 package 더블클릭

🔸 오류
It is highly recommended to fix these problems because they threaten the stability of your build. For this reason, future Maven versions might no longer support building such malformed projects.

아래와 같이 설정하여 오류 해결

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
  1. aws에서 포트 오픈
    사용할 포트인 8081로 설정

  2. git bash에서
    aws 인스턴스 접속 및 권한 설정(sudo su -)

  3. Maven 설치

apt update
apt install maven
  1. 작업한 Hospital Project 깃 클론
    git clone https://github.com/KoKwanwun/LikeLion

  2. 프로젝트로 접근하여 Dockerfile 생성

    1. vim Dockerfile
    2. i입력하여 입력가능하도록
    3. 아래 코드 입력
    FROM openjdk:11-jdk-slim
    VOLUME /tmp
    ADD /target/*.jar app.jar
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
    1. ESC
    2. Shift + z z
    3. 파일 생성 완료
  3. mvn package : .jar파일 생성
    (Test과정 스킵 코드 : mvn -DskipTests=true package)

  4. docker build -t springboot-jdbc-template .
    : Docker파일 빌드(.앞뒤로 공백)

  5. Docker 실행(-e는 환경변수 설정)
    docker run -p 8081:8081 -e SPRING_DATASOURCE_URL=[DNS주소 및 DB] -e SPRING_DATASOURCE_PASSWORD=[비밀번호] springboot-jdbc-template

  6. swagger 접속
    http://[DNS주소]:8081/swagger-ui/

발생한 오류

생성한 인스턴스(ubuntu)의 자바 버전이 11인데 내가 작업한 자바 환경은 17

🟢 해결방법

  • 서버에 자바 17설치
  • 작업한 자바 환경 버전을 11로 설정한 후 Push하고, 다시 git clone하기

0개의 댓글