[LIKELION] 221013

고관운·2022년 10월 13일

회고

😄 느낀점

  • 알고리즘 실습을 하면서 버블정렬과 선택정렬 사이에서 헷갈린 부분이 있었다.
  • 어제 수업을 듣지 못해서 gradle 빌드에서 살짝 막혔지만 공부를 미리 해둬서 빠르게 따라잡을 수 있었다.
  • 자바에서 데이터를 전처리하여 MySql로 가져오는 실습을 하며, 편리하다고 생각했다.

😁 목표

  • CodeUp 100제 (1061~1070 풀기)
  • 삽입정렬 예습
  • Gradle build + test class 공부

[멋쟁이사자처럼] 4주차 - DB, Java와 연동(7, 50~57)

알고리즘 기초(Bubble Sort)

Bubble Sort(버블정렬)이란

서로 인접한 두 원소를 검사하여 정렬하는 알고리즘

Bubble Sort을 하기 위해 알아야하는 것

  1. swap 자리바꾸기
  2. 중첩 for문
  3. 중첩 for문의 control

실습

구현 시뮬레이션
1. 각 Step의 첫번째 값을 고정
2. 고정된 값(파란색 칸)과 뒤에 있는 값(빨간색 칸)을 각각 비교하여 고정된 값보다 작은 값이 있다면 Swap
3. 각 Step이 끝나면, 고정된 값에 비교한 숫자 중 가장 작은 숫자가 옴

의문사항
Bubble Sort VS Selection Sort
Bubble Sort : 인접한 두 원소를 비교
Selection Sort : 최솟값을 구한 후 앞쪽의 값과 교환
➡ 강사님의 방식은 두 방식을 모두 사용한 것과 같음

조건
1. static method는 main에만 사용해라
2. int형 array를 return하고 int형 array를 받는 sort() method를 만들어라

import java.util.Arrays;

public class BubbleSort01 {
    int[] sort(int[] arr) {
        int temp;

        for(int i = 0; i < arr.length; i++) {
            for(int j = i + 1; j < arr.length; j++) {
                if(arr[i] > arr[j]){
                    // swap
                    temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }

        return arr;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{7, 2, 3, 9, 28, 11};
        // 오름차순 또는 내림차순으로 정렬하는 알고리즘을 짜보세요.
        BubbleSort01 bubbleSort01 = new BubbleSort01();
        int[] resultArr = bubbleSort01.sort(arr);

        System.out.println(Arrays.toString(resultArr));
    }
}

시간복잡도
버블 정렬은 이중 for문으로 구현되어 시간복잡도가 O(n^2)임

Hospital Project

Gradle 빌드

이번 Hospital Project는 Gradle 빌드를 하여 따로 프로젝트를 제작
왜 Gradle 빌드를 사용할까? ➡ Test Case를 작성하고 실행하기가 좋다.

Gradle 프로젝트 생성 방법
File ➡ New ➡ Project ➡ Build system : Gradle로 생성

어노테이션(annotaion)

  • Test 코드 작성할 때 사용함. (@가 붙은 코드)
  • 특정 메소드에 기능을 넣어줌
  • 테스트 성공시 @DisplayName을 통해 적어줌

Test Case 생성

특정 Class의 Test Case를 만들고 싶을 때
Class명에 커서 위치 ➡ Alt + Enter ➡ Create Test

🔴 Test Case가 Test 디렉토리에 생성되지 않는 경우
Test 디렉토리가 없을 때 : src 하위에 Test 디렉토리 생성 ➡ Test 오른쪽 버튼 ➡ mark directory as ➡ Test Sources Root ➡ Test 디렉토리 하위에 java 디렉토리 생성 ➡ 이후 다시 Test Case 생성 시도

화면 Split
Class와 Test Case를 동시에 화면에 띄워서 효율적으로 작업
방법 : 작업 Tab의 java파일 오른쪽 버튼 ➡ Split Right, Split Down 중 선택하여 사용

데이터 설명

서울시 병의원 위치 정보
해당 데이터는 서울시 병의원의 위치 정보를 가지고 있음
이 데이터 중 일부를 사용하여 MySql에서 활용 예정

변수명데이터 설명인덱스
id기관ID0
address주소1
district주소에서 시구 추출1에서 추출
category병원분류2
emergencyRoom응급실운영여부6
name기관명10
subdivision세부분과(기관명에서 추출)10에서 추출

각 변수에 맞는 데이터를 파싱하여 Test

고려사항
1. 데이터에서 parsing시, "문제 발생
str = str.replaceAll("\"", ""); : "를 공백으로 대체하여 오류 방지

2. district
생성자에서 작업
주소를 공백으로 나눈 후 시와 구만 가져와서 넣어줌

String[] splitted = this.address.split(" ");
this.district = String.format("%s %s", splitted[0], splitted[1]);

3. subdivision
parse 작업할 때 함수 호출
subdivisions에 세부분과를 리스트하여 해당 내용이 기관명에 포함되어 있으면 해당 세부분과 리턴, 없다면 공백 리턴
private String getSubdivision(String name) {
        String[] subdivisions = {"소아과", "피부과", "성형외과", "정형외과", "척추", "교정", "산부인과", "관절", "봉합", "화상", "골절", "영유아", "안과", "가정의학과", "비뇨기과", "치과", "내과", "외과"};

        for (String subdivision : subdivisions) {
            if(name.contains(subdivision)){
                return subdivision;
            }
        }
        return "";
    }

Parsing Test 코드
🟢 2개의 정보를 Test하기 위해 assertHospital 메소드 생성
➡ parsing 데이터와 실제 데이터가 맞는지 비교

	String line1 = "\"A1120837\",\"서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)\",\"C\",\"의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"외과: 상시진료 내과는 당분간 휴진\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"방이역 1번출구 바로옆 굿모닝 신한증권 뒷건물\",\"가산기대찬의원\",\"02-6267-2580\",\"02-920-5374\",\"1930\",\"1930\",\"1930\",\"1930\",\"1930\",\"1500\",\"1500\",\"1500\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"1000\",\"1000\",\"085\",\"11\",\"126.88412249700781\",\"37.4803938036867\",\"2022-04-07 14:55:00.0\"";
    String line2 = "\"A1117873\",\"서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)\",\"N\",\"치과의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"대표번호1 지역번호 추가20170118150453\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"서월치안센터 인근 청암빌딩 5층\",\"가로수치과의원\",\"02-882-2750\",\"02-920-5374\",\"1900\",\"2100\",\"1900\",\"2100\",\"1900\",\"1400\",\"1500\",\"1500\",\"1000\",\"1000\",\"0930\",\"1400\",\"1000\",\"1000\",\"1000\",\"1000\",\"087\",\"76\",\"126.92937673003041\",\"37.48191798611885\",\"2022-01-07 14:54:55.0\"";

    private void assertHospital(Hospital hospital, String eId, String eAddress, String eDistrict, String eCategory, int eEmergencyRoom, String eName, String eSubdivision) {
		// Id 파싱 확인
        Assertions.assertEquals(eId, hospital.getId());

        // Address 파싱 확인
        Assertions.assertEquals(eAddress, hospital.getAddress());

        // District 파싱 확인
        Assertions.assertEquals(eDistrict, hospital.getDistrict());

        // Category 파싱 확인
        Assertions.assertEquals(eCategory, hospital.getCategory());

        // EmergencyRoom 파싱 확인
        Assertions.assertEquals(eEmergencyRoom, hospital.getEmergencyRoom());

        // Name 파싱 확인
        Assertions.assertEquals(eName, hospital.getName());

        // Subdivision 파싱 확인
        Assertions.assertEquals(eSubdivision, hospital.getSubdivision());
    }

    // annotation
    @Test
    @DisplayName("Parsing doing well")
    void hospitalParsing() {
        HospitalParser hospitalParser = new HospitalParser();
        // ctrl + tab
        // line 1
        assertHospital(hospitalParser.parse(this.line1),
                "A1120837", "서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)",
                "서울특별시 금천구", "C", 2, "가산기대찬의원", "");
        // line 2
        assertHospital(hospitalParser.parse(this.line2),
                "A1117873", "서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)",
                "서울특별시 관악구", "N", 2, "가로수치과의원", "치과");
    }

.sql 파일 생성

Sql Insert문 형식 만드는 코드

public String getSqlInsertQuery() {
        this.address = this.address.replace("'", "");
        String query = "INSERT INTO `likelion-db`.`seoul_hospital` (`id`,`address`,`district`,`category`,`emergency_room`,`name`,`subdivision`) " +
                "VALUES (\"" + this.id + "\",\"" + this.address + "\",\"" + this.district + "\",\"" + this.category + "\"," +
                this.emergencyRoom + ",\"" + this.name + "\",";
        if(this.subdivision != null) {
            query += "\"" + this.subdivision + "\");";
        } else {
            query += "\");";
        }
        return query;
    }

🟢 Hospital Class 내부에 위치하여 생성자로 받은 변수 데이터를 넣어서 Insert문 형식인 String을 리턴

.sql 파일 저장

	List<String> lines = new ArrayList<>();
    for (Hospital hospital : hospitals) {
        lines.add(hospital.getSqlInsertQuery());
    }
    String sqlFilename = "hospital_insert.sql";
    hospitalLineReader.createANewFile(sqlFilename);
    hospitalLineReader.writeLines(lines, sqlFilename);

🟢 Hospital 형식의 데이터를 getSqlInsertQuery 메소드를 거쳐 String 배열 생성
🟢 sqlFilename을 가진 파일 생성 후 파일 쓰기

최종코드

  1. Hospital : 데이터를 저장할 형식을 담고 있는 클래스
  public class Hospital {
    private String id;
    private String address;
    private String district;
    private String category;
    private int emergencyRoom;
    private String name;
    private String subdivision;

    public Hospital(String id, String address) {
        this.id = id;
        this.address = address;
    }

    public Hospital(String id, String address, String category, int emergencyRoom, String name, String subdivision) {
        this.id = id;
        this.address = address;

        String[] splitted = this.address.split(" ");
        this.district = String.format("%s %s", splitted[0], splitted[1]);

        this.category = category;
        this.emergencyRoom = emergencyRoom;
        this.name = name;
        this.subdivision = subdivision;
    }

    public String getSqlInsertQuery() {
        this.address = this.address.replace("'", "");
        String query = "INSERT INTO `likelion-db`.`seoul_hospital` (`id`,`address`,`district`,`category`,`emergency_room`,`name`,`subdivision`) " +
                "VALUES (\"" + this.id + "\",\"" + this.address + "\",\"" + this.district + "\",\"" + this.category + "\"," +
                this.emergencyRoom + ",\"" + this.name + "\",";
        if(this.subdivision != null) {
            query += "\"" + this.subdivision + "\");";
        } else {
            query += "\");";
        }
        return query;
    }

    public String getId() {
        return id;
    }

    public String getAddress() {
        return address;
    }

    public String getDistrict() {
        return district;
    }

    public String getCategory() {
        return category;
    }

    public int getEmergencyRoom() {
        return emergencyRoom;
    }

    public String getName() {
        return name;
    }

    public String getSubdivision() {
        return subdivision;
    }
}

  1. HospitalParser : 데이터를 parsing하여 Hospital 객체를 생성하는 클래스
public class HospitalParser implements Parser<Hospital> {
    private String getSubdivision(String name) {
        String[] subdivisions = {"소아과", "피부과", "성형외과", "정형외과", "척추", "교정", "산부인과", "관절", "봉합", "화상", "골절", "영유아", "안과", "가정의학과", "비뇨기과", "치과", "내과", "외과"};

        for (String subdivision : subdivisions) {
            if(name.contains(subdivision)){
                return subdivision;
            }
        }
        return "";
    }

    @Override
    public Hospital parse(String str) {
        // "에 대한 에러 방지
        str = str.replaceAll("\"", "");
        String[] splitted = str.split(",");

        String name = splitted[10];
        String subDivision = getSubdivision(name);

        return new Hospital(splitted[0], splitted[1], splitted[2], Integer.parseInt(splitted[6]), name, subDivision);
    }
}

  1. LineReader : 데이터를 읽기, 파일 생성, 파일 쓰기 메소드를 담고있는 클래스
import com.line.parser.Parser;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class LineReader<T> {
    Parser<T> parser;

    boolean isRemoveColumnName = true;

    public LineReader(Parser<T> parser) {
        this.parser = parser;
    }

    // 첫행 받는여부를 생성자로 받을 수 있음
    public LineReader(Parser<T> parser, boolean isRemoveColumnName) {
        this.parser = parser;
        this.isRemoveColumnName = isRemoveColumnName;
    }

    List<T> readlines(String filename) throws IOException {
        List<T> result = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader(filename));
        String str;

        // 첫 행 날리기
        if(isRemoveColumnName) {
            br.readLine();
        }

        while((str = br.readLine()) != null) {
            result.add(parser.parse(str));
        }
        return result;
    }

    public void createANewFile(String filename) throws IOException {
        File file = new File(filename);
        file.createNewFile();
        System.out.println("파일 생성 되었는지?:" + file.exists());
    }

    public void writeLines(List<String> lines, String filename) throws IOException{
        File file = new File(filename);
        FileWriter fileWriter = new FileWriter(file);
        for (int i = 0; i < lines.size(); i++) {
            fileWriter.write(lines.get(i) + "\n");
        }
        fileWriter.flush();
        fileWriter.close();
        System.out.println("success");
    }
}

  1. Main : 파일을 읽어서 sql파일을 생성하는 Main 클래스
import com.line.domain.Hospital;
import com.line.parser.HospitalParser;

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

public class Main {
    public static void main(String[] args) throws IOException {
        LineReader<Hospital> hospitalLineReader = new LineReader<>(new HospitalParser());
        String filename = "D:\\고관운 자료\\멋쟁이사자처럼\\백엔드스쿨 2기 교본\\220919\\4주차\\서울시 병의원 위치 정보.csv";
        List<Hospital> hospitals = hospitalLineReader.readlines(filename);

        System.out.println(hospitals.size());

        List<String> lines = new ArrayList<>();
        for (Hospital hospital : hospitals) {
            lines.add(hospital.getSqlInsertQuery());
        }
        String sqlFilename = "hospital_insert.sql";
        hospitalLineReader.createANewFile(sqlFilename);
        hospitalLineReader.writeLines(lines, sqlFilename);
    }
}

  1. HospitalParserTest : 각 변수마다 잘 Parsing 했는지 테스트, sql insert문으로 잘 전처리했는지 테스트하는 Test 클래스
import com.line.domain.Hospital;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class HospitalParserTest {
    String line1 = "\"A1120837\",\"서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)\",\"C\",\"의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"외과: 상시진료 내과는 당분간 휴진\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"방이역 1번출구 바로옆 굿모닝 신한증권 뒷건물\",\"가산기대찬의원\",\"02-6267-2580\",\"02-920-5374\",\"1930\",\"1930\",\"1930\",\"1930\",\"1930\",\"1500\",\"1500\",\"1500\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"0900\",\"1000\",\"1000\",\"085\",\"11\",\"126.88412249700781\",\"37.4803938036867\",\"2022-04-07 14:55:00.0\"";
    String line2 = "\"A1117873\",\"서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)\",\"N\",\"치과의원\",\"G099\",\"응급의료기관 이외\",\"2\",\"대표번호1 지역번호 추가20170118150453\",\"서울시 송파구 문정동 장지동 법조단지 위례 가락동 가락시장역 위치 삼성서울병원 외래교수 출신 구강외과 전문의 진료 진료과목 - 임플란트 치조골 뼈이식 수술 매복 사랑니 발치 턱관절 악관절 질환의 치료 교정 치료 및 기타 보존 보철(크라운 브릿지 인레이) 신경치료\",\"서월치안센터 인근 청암빌딩 5층\",\"가로수치과의원\",\"02-882-2750\",\"02-920-5374\",\"1900\",\"2100\",\"1900\",\"2100\",\"1900\",\"1400\",\"1500\",\"1500\",\"1000\",\"1000\",\"0930\",\"1400\",\"1000\",\"1000\",\"1000\",\"1000\",\"087\",\"76\",\"126.92937673003041\",\"37.48191798611885\",\"2022-01-07 14:54:55.0\"";

    private void assertHospital(Hospital hospital, String eId, String eAddress, String eDistrict, String eCategory, int eEmergencyRoom, String eName, String eSubdivision) {
// Id 파싱 확인
        Assertions.assertEquals(eId, hospital.getId());

        // Address 파싱 확인
        Assertions.assertEquals(eAddress, hospital.getAddress());

        // District 파싱 확인
        Assertions.assertEquals(eDistrict, hospital.getDistrict());

        // Category 파싱 확인
        Assertions.assertEquals(eCategory, hospital.getCategory());

        // EmergencyRoom 파싱 확인
        Assertions.assertEquals(eEmergencyRoom, hospital.getEmergencyRoom());

        // Name 파싱 확인
        Assertions.assertEquals(eName, hospital.getName());

        // Subdivision 파싱 확인
        Assertions.assertEquals(eSubdivision, hospital.getSubdivision());
    }

    // annotation
    @Test
    @DisplayName("Parsing doing well")
    void hospitalParsing() {
        HospitalParser hospitalParser = new HospitalParser();
        // ctrl + tab
        // line 1
        assertHospital(hospitalParser.parse(this.line1),
                "A1120837", "서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)",
                "서울특별시 금천구", "C", 2, "가산기대찬의원", "");
        // line 2
        assertHospital(hospitalParser.parse(this.line2),
                "A1117873", "서울특별시 관악구 신원로 38 5층 (신림동 청암빌딩)",
                "서울특별시 관악구", "N", 2, "가로수치과의원", "치과");
    }

    @Test
    @DisplayName("insert쿼리를 잘 만드는지 test")
    void makeSqlQueryTest() {
        HospitalParser hospitalParser = new HospitalParser();
        Hospital hospital = hospitalParser.parse(this.line1);
        String sql = "INSERT INTO `likelion-db`.`seoul_hospital` (`id`,`address`,`district`,`category`,`emergency_room`,`name`,`subdivision`) " +
                "VALUES (\"A1120837\",\"서울특별시 금천구 벚꽃로 286 삼성리더스타워 111~114호 (가산동)\",\"서울특별시 금천구\",\"C\"," + 2 + ",\"가산기대찬의원\",\"\");";
        Assertions.assertEquals(sql, hospital.getSqlInsertQuery());
    }
}

생성한 .sql 파일로 MySQL에 Insert

  1. MySQL Workbench 접속
  2. aws 인스턴스로 만든 MySQL Connections에 접속
  3. File ➡ Open SQL Script ➡ Java로 생성한 .sql 파일 선택
  4. 번개 모양 클릭하여 실행

0개의 댓글