분류 전체보기 (103)

 

 

 

(이게 왜 레벨 2가 아닌 레벨 1일까요?..)

 

문제

 

문제링크

두 숫자 X, Y가 주어질 때 X와 Y에 공통으로 있는 숫자를 모두 찾아서 그 숫자들을 조합하여 만들수 있는 최댓값을 리턴하는 함수를 작성한다.

같은 숫자가 중복되어 있을 경우 중복된 개수만큼 붙인다.

 

 

해결과정

처음엔 문제를 잘못 이해하고 중복을 없애려고 Set을 사용했다가 문제를 잘못 이해한 것을 인지하고 List로 바꿔서 구현했었습니다. ㅎ

그러나 List를 사용 시 몇 가지 테스트에서 통과를 하지 못하여 갈아엎고 ㅎ 배열로 풀었습니다. 😂

입력받은 String을 각각 char형 배열로 변환한 후 오름차순으로 정렬을 했습니다.

오름차순으로 각각 정렬한 이유는 0번 인덱스부터 배열의 끝까지 비교하면서 공통으로 있는 수를 찾으려 했기 때문입니다.

우선은 두 개의 배열을 반복문에서 동시에 순회하면서 비교할 것이기 때문에 index를 담을 변수를 선언 후 0으로 초기화해줍니다.

저는 반복문으로 for문이 아닌 while문을 사용하였는데 따로 x, y 인덱스 변수가 있기 때문에 인덱스를 증가시켜줄 필요가 없어 조건만 넣은 while 문을 사용하였습니다.

while 문의 조건은 'x인덱스가 x배열의 길이 보다 작고 y인덱스가 y배열의 길이보다 작다'로 설정하였습니다.

이미 x배열과 y배열은 정렬이 되어 있기 때문에 앞에서부터 비교하면서 값이 더 작은 배열의 인덱스의 값을 올려주었습니다.

그리고 값이 같을 경우 해당 값을 StringBuffer에 붙여주고 두 배열의 index를 동시에 증가시켜주었습니다.

(StringBuffer를 사용한 이유는 오름차순을 내림차순으로 바꿔주기 위해 reverse()를 사용할 것이기 때문입니다!)

이렇게해서 하나의 배열이라도 맨 끝에 다다르면 반복을 중지합니다.

이렇게 되면 모든 공통값이 오름차순으로 들어가게 되서 reverse()를 해줍니다.

여기까지만 작성하고 리턴하게 되면 몇가지를 통과하지 못하게 되는데..

0만 있을 경우엔 (예를 들어 "000" 같은 경우) "0"으로 변환해주어야 합니다.

만약에 결과 문자열에 0만 있다면 내림차순이기 때문에 맨 앞에 0이 올 것입니다.

따라서 맨 앞의 문자가 0인지 조건문으로 확인하여 0이라면 "0"만 리턴하면 됩니다.

이렇게 배열로 하면 시간복잡도가 O(n)으로 나쁘진 않은 편? 입니다. (이보다 좋은 방법이 있겠지만 떠오르지 않았습니다.)

 

 

코드

import java.util.Arrays;
import java.lang.StringBuffer;

class Solution {
    public String solution(String x, String y) {
        StringBuffer answer = new StringBuffer("");
        char[] xArr = x.toCharArray();
        char[] yArr = y.toCharArray();
        Arrays.sort(xArr);
        Arrays.sort(yArr);
        int xIdx = 0;
        int yIdx = 0;
        while (xIdx < xArr.length && yIdx < yArr.length) {
            if (xArr[xIdx] == yArr[yIdx]) {
                answer.append(String.valueOf(xArr[xIdx]));
                xIdx++;
                yIdx++;
            } else if (xArr[xIdx] < yArr[yIdx]) {
                xIdx++;
            } else if (xArr[xIdx] > yArr[yIdx]) {
                yIdx++;
            }
        }
        if (answer.length() < 1)    return "-1";
        answer.reverse();
        String result = answer.toString();
        if (result.charAt(0) == '0') return "0";
        return result;
    }
}

 

 

실행 결과

✌️

 

 

 

깃허브 코드 보러가기

 

읽어주셔서 감사합니다. 🤗 좋은하루 되세요~

 

 

 

 

문제

Merge Sort를 이용하여 int 형 배열을 정렬하시오.

조건

  • Arrays.sort 사용 금지
  • 병합 정렬을 이용하여 정렬

 

병합 정렬이란??

우선 입력받은 배열을 길이가 1인 배열이 될 때까지 계속 반으로 나눈다.

나눈 배열을 2개씩 병합하여 정렬한다.

길이가 2인 배열 병합 -> 길이가 4인 배열 병합 -> 길이가 8인 배열 병합 ...

병합할 때마다 요소들을 정렬한다.

 

출처 : GeeksforGeeks merge Algorithm

 

 

해결 방법

'반으로 나눈다 -> 병합한다' 라는 반복과정이 있기 때문에 반복, 재귀 중에 한가지로 해결할 수 있습니다.

저는 재귀를 활용하여 풀었습니다.

배열을 중간을 기준으로 나눠야 하기 때문에 length / 2 를 기준으로 배열을 left, right 두부분으로 분리하였습니다.

배열의 길이가 1이 될 때까지 이과정을 반복해야 하기 때문에 left, right 배열을 초기화하는 과정에서 바로 재귀를 호출하였습니다.

브레이크 포인트로 길이가 1이하가 될 경우 멈추도록 했습니다.

이제 쪼개진 배열을 오름차순으로 병합하면 됩니다.

이때 left 배열과 right 배열은 각각 이미 정렬이 된 상태이기 때문에 두 배열을 앞에서부터 비교하면서 더 작은 쪽의 요소를 넣어주고 인덱스 카운트를 올려줍니다.

그럼 각각의 인덱스를 기억해야하기 때문에 각각 인덱스를 담을 leftIdx, rightIdx를 선언합니다.

그리고 반복문으로 정렬할 배열의 길이만큼 반복해주면서 두 배열 중 작은 요소를 결과 배열에 넣고 해당 배열의 인덱스를 +1 시켜줍니다.

여기서 배열의 길이를 넘어버리면 안되기 때문에 배열의 길이를 넘을 경우 반대쪽 배열의 남은 요소를 모두 붙여넣고 반복을 종료시키도록 합니다.

 

 

코드

 

import java.util.Arrays;

public class MergeSort {
    public int[] mergeSort(int[] arr) {
        if (arr.length < 2) {
            return arr;
        }
        int middle = arr.length / 2;
        int[] left = mergeSort(Arrays.copyOfRange(arr, 0, middle));
        int[] right = mergeSort(Arrays.copyOfRange(arr, middle, arr.length));

        arr = new int[left.length + right.length];
        int leftIdx = 0;
        int rightIdx = 0;
        for (int i = 0; i < arr.length; i++) {
            if (leftIdx >= left.length) {
                System.arraycopy(right, rightIdx, arr, i, right.length - rightIdx);
                break;
            } else if (rightIdx >= right.length) {
                System.arraycopy(left, leftIdx, arr, i, left.length - leftIdx);
                break;
            } else if(left[leftIdx] > right[rightIdx]) {
                arr[i] = right[rightIdx];
                rightIdx++;
            } else {
                arr[i] = left[leftIdx];
                leftIdx++;
            }
        }
        return arr;
    }
}

 

 

 

깃허브 코드 보러가기

 

감사합니다. 좋은 하루 되세요. 🤗

 

 

 

 

 

문제

 

문제 링크

카카오 성격 유형 검사지를 만들려고 합니다.

성격 유형은 아래와 같습니다.

지표 번호 성격 유형
1번 지표 라이언형(R), 튜브형(T)
2번 지표 콘형(C), 프로도형(F)
3번 지표 제이지형(J), 무지형(M)
4번 지표 어피치형(A), 네오형(N)

각각의 지표에 대해서 점수가 높은 알파벳을 조합하여 성격 유형 결과가 나옵니다. -> mbti를 생각하면 됨

ex) RCMA

 

검사지에는 총 n개의 질문이 있고 선택지 총 7개 입니다.

  • 매우 비동의
  • 비동의
  • 약간 비동의
  • 모르겠음
  • 약간 동의
  • 동의
  • 매우 동의

 

검사자의 선택에 따라(동의/비동의) 한가지 지표의 한 개의 성격 유형에 대한 점수를 얻습니다. (1번 지표일 경우 비동의가 R, 동의가 T일 수도 있고, 동의가 R, 비동의가 T일 수 있음)

R(라이언형)이 비동의, T(튜브형)이 동의일 경우 점수는 아래와 같습니다.

답변 점수
매우 비동의 라이언형 3점
비동의 라이언형 2점
약간 비동의 라이언형 1점
모르겠음 어떤 성격 유형도 점수를 얻지 않음
약간 동의 튜브형 1점
동의 튜브형 2점
매우 동의 튜브형 3점

 

점수가 높은 지표를 조합하여 성격유형 검사 결과를 출력합니다.

 

입력

  • survey : survey[i] 번째 문자열은 i+1 번째 문제의 성격유형을 의미 (2글자로 1번째 문자는 비동의, 2번째 문자는 동의에 해당하는 성격유형)
    • ex) "RT", "MJ"
  • choices : choices[i]는 검사자가 선택한 i+1번째 질문의 선택지

 

 

해결 과정

 

처음에는 Map을 이용하여 풀려고 했으나 Map으로 푸는 건 너무 흔할 것 같아 새로운 시도를 해보고 싶어 배열로 풀었습니다.

 

 

코드

 

public String solution(String[] survey, int[] choices) {
    String answer = "";
    // 설문자의 점수를 담을 정수형 배열 선언, 차례대로 R, C, J, A 형의 점수, 반대 점수는 음수로 더한다.
    int[] scores = new int[4];

    // 설문지 점수표를 담을 정수형 배열 선언, i번째 인덱스의 숫자가 i점의 점수
    int[] scoreTable = {0, 3, 2, 1, 0, 1, 2, 3};

    // 계산할 점수 지표를 담을 캐릭터형 선언
    char indicator;

    // 질문지의 길이만큼 순회하여 choices의 i번째 요소를 꺼낸다.
    for(int i = 0; i < choices.length; i++) {
        int choice = choices[i];
        // 꺼낸 점수가 4일 경우 continue
        if (choice == 4) continue;
        // 꺼낸 점수가 1~3일 경우
        if (choice < 4) {
            // survey[i]의 0번째 캐릭터형이 점수 지표
            indicator = survey[i].charAt(0);
        } else {  // 꺼낸 점수가 5~7일 경우
            // survey[i]의 1번째 캐릭터형이 점수 지표
            indicator = survey[i].charAt(1);
        }
        // indicator 에 따라 점수를 계산
        switch(indicator) {
            case 'R':
                scores[0] += scoreTable[choice];
                break;
            case 'T':
                scores[0] -= scoreTable[choice];
                break;
            case 'C':
                scores[1] += scoreTable[choice];
                break;
            case 'F':
                scores[1] -= scoreTable[choice];
                break;
            case 'J':
                scores[2] += scoreTable[choice];
                break;
            case 'M':
                scores[2] -= scoreTable[choice];
                break;
            case 'A':
                scores[3] += scoreTable[choice];
                break;
            case 'N':
                scores[3] -= scoreTable[choice];
                break;
            default: break;
        }
    }
    // 점수 배열의 0번째 인덱스가 0이상인 경우 결과 문자열에 R, 음수일경우 T를 붙인다.
    if (scores[0] >= 0) {
        answer += "R";
    } else answer += "T";
    // 점수 배열의 1번째 인덱스가 0이상인 경우 결과 문자열에 C, 음수일경우 F를 붙인다.
    if (scores[1] >= 0) {
        answer += "C";
    } else answer += "F";
    // 점수 배열의 2번째 인덱스가 0이상인 경우 결과 문자열에 J, 음수일 경우 M을 붙인다.
    if (scores[2] >= 0) {
        answer += "J";
    } else answer += "M";
    // 점수 배열의 3번째 인덱스가 0이상인 경우 결과 문자열에 A, 음수일 경우 N을 붙인다.
    if (scores[3] >= 0) {
        answer += "A";
    } else answer += "N";
    return answer;
}

 

 

 

깃허브 코드 보러가기

 

 

 

 

 

문제

정수를 요소로 갖는 배열을 입력받아 오름차순으로 정렬하여 리턴합니다.

조건

  • sort 메서드 사용 금지
  • 퀵 정렬을 구현
  • 오름차순으로 정렬

 

 

해결과정

퀵 정렬을 정확하게 모르기 때문에 구현할 수가 없어 퀵 정렬 방법만 검색하여 참고한 후 코드는 직접 구현하였습니다.

퀵 정렬은 pivot을 기준으로 작은 수들, 큰 수들을 각각 새로운 배열에 넣는 과정을 반복하여 정렬하는 방식입니다.

시간복잡도는 O(nlog n) 에서 최악의 경우 O(n^2) 입니다.

검색한 퀵 정렬의 개념을 토대로 재귀를 이용하여 간단하게 로직을 구현했습니다.

pivot은 주로 맨 끝 요소를 기준으로 하는 것으로 보여서 입력받은 맨끝요소를 pivot으로 하고

배열의 나머지 요소를 순회하면서 작은 수와 큰 수를 각각 나눠 리스트에 집어넣습니다.

(배열이 아닌 리스트를 사용한 이유는?

배열은 사이즈를 알고 있어야 생성할 수 있는데 pivot을 기준으로 큰 수가 몇 개이고 작은 수가 몇 개인지 순회하기 전까진 알 수 없어

번거롭더라도 사이즈가 가변하는 리스트에 넣은 후 순회를 마치면 toArray로 배열로 변환해주었습니다.)

그 후에 작은 수 배열을 재귀로 집어넣고 큰 수 배열도 마찬가지로 재귀에 집어넣습니다.

재귀 브레이크 포인트로 입력받은 배열의 길이가 1이하일 경우는 그대로 리턴하고 2일 경우는 정렬 후 리턴하도록 했습니다.

마지막으로 배열을 작은 수 배열 + pivot + 큰 수 배열 순으로 붙여서 결과 배열을 만든 후 리턴하였습니다.

 

 

코드

public int[] quickSort(int[] arr) {
    if (arr.length <= 1) {
        return arr;
    }
    if (arr.length == 2) {
        if (arr[0] > arr[1]) {
            int a = arr[0];
            arr[0] = arr[1];
            arr[1] = a;
        }
        return arr;
    }

    int[] answer = new int[arr.length];
    List<Integer> smallNums = new ArrayList<>();
    List<Integer> bigNums = new ArrayList<>();

    int pivot = arr[arr.length - 1];
    for (int i = 0; i < arr.length - 1; i++) {
        if (arr[i] < pivot) {
            smallNums.add(arr[i]);
        } else bigNums.add(arr[i]);
    }
    int[] smallArr = quickSort(smallNums.stream().mapToInt(Integer::intValue).toArray());
    int[] bigArr = quickSort(bigNums.stream().mapToInt(Integer::intValue).toArray());
    System.arraycopy(smallArr, 0, answer, 0, smallArr.length);
    answer[smallArr.length] = pivot;
    System.arraycopy(bigArr, 0, answer, smallArr.length + 1, bigArr.length);
    return answer;
}

 

 

 

깃허브 코드 보러가기

 

 

 

감사합니다. 좋은하루 되세요. 😄

 

 

벌써 메인 프로젝트 기간 끝.

 

메인 프로젝트 시작한게 엊그제 같은데 ... 벌써 내일이면 데모데이네요.

메인프로젝트는 9월 7일에 시작해서 10월 12일까지 한달밖에 안되는 짧은기간이었습니다.

이제 막 코딩시작했던거 같은데 벌써 끝난다니..!! 아직 서비스 완성도 안됐는데..!!!! 

 

벌써?!!?!?!?!?

 

저희팀은 "냥빌리지🐱"라는 고양이 SNS 겸 커뮤니티 서비스를 기획하고 제작하였습니다.

냥빌리지 메인화면!

간단하게 팀원들과 회고를 진행하면서 좋았던 점과 아쉬웠던 점 등을 정리해보았습니다.

 

 

 

기획과 설계


기획 단계에서 한번 밖에 없을지도 모르는 기회라 생각했기에 모두가 애정과 열정을 가지고 만들 수 있는 서비스를 기획하자고 결정하였습니다. 그래서 모두가 하고싶은 서비스를 기획하다보니 "고양이"라는 공통 관심사에서 "냥빌리지"라는 서비스가 탄생하게 되었습니다.

기획서 작성을 마치고 API 명세서 간단하게 초안을 작성하면서 ERD, 피그마 등을 함께 만들었는데요. 이 과정에서 저희가 기간은 생각하지 않고 넣고 싶은 기능을 넣다보니 서비스가 좀 커져버렸습니다.

 

피그마

 

ERD (테이블 23개..)

 

설계하고나서 저희가 이걸 다 만들 수 있을까..? 약간 걱정되었지만 최선을 다해서 할 수 있는 만큼 한 것 같습니다.

 

 

♥️ 좋았던 점


팀원과의 협업, 오버커뮤니케이션 🔥

가장 좋았던 점이라고 하면...

역시 모든 문제를 팀원들과 협업하며 해결해 나갔던 것이라고 꼽을 수 있을 것 같습니다.

저는 혼자 일하는 것보다 누군가와 함께 일하는 것을 훨씬 선호하는 편입니다. 그래서 그런지 몰라도 프로젝트하면서 커뮤니케이션으로 힘든 부분은 아예 없었고 즐겁기만 했습니다. 팀원 한분한분이 열정을 가지고 임해주셨고 쿠션어를 사용하며 서로에게 상처가 되지 않게 피드백을 주었던 것 같습니다. 그리고 다들 팀 전체를 생각하는 마음에서 지금 내 의견이 기각되더라도 더 좋은 의견이 있다면 수용하는 태도로 임해주셨습니다. 한분한분 덕분에 커뮤니케이션에 아무런 문제없이 즐겁게 프로젝트 진행할 수 있었습니다.

또한 혼자라면 아마 오래걸렸을 문제들을 같이 공유하고 고민하며 더 나은 해결책을 찾을 수 있었고, 서로의 부족한 점들을 매꿀 수 있었습니다. 👍

 

 

🚀 배포 자동화 구축

기술적으로 좋았던 점이라고 하면...

코딩 초반에 CI/CD를 먼저 구축한게 너무 뿌듯했습니다. 배포 자동화를 먼저 셋팅 해놓자는 의견에 다들 찬성해서 프론트엔드와 백엔드 다같이 배포 환경을 우선 셋팅하였고 깃허브 액션과 Code Deploy를 사용하여 배포 자동화를 구축했습니다. 이과정에서 수많은 우여곡절(?)이 있긴 했지만 그래도 같이 하기에 빠르게 해결하고 셋팅할 수 있었습니다.

짧은 프로젝트 기간동안 배포자동화를 구축하기란 쉽지 않지만 해냈다는 성취감에 너무 뿌듯했습니다. 😁

배포자동화를 구축한 덕분에 이후에 변경사항이 바로바로 서버에 반영되서 너무 편했습니다.!!

 

 

그외 다양한 좋았던 점들

그외에도 셀수 없이 많은 좋았던 점들이 있었던 것 같습니다.

  • 디자인이 너무 예쁘게 잘나왔던 점
  • 모두가 항상 적극적으로 의견을 제시했던 점
  • 다들 밤새가면서 열정적으로 프로젝트 하셔서 열정 에너지를 얻을 수 있었던 점
  • CORS 에러 다같이 해결했던 것
  • 처음보는 문제(에러)에도 다들 적극적이고 긍정적이게 해결하려한 점
  • 다들 예스맨이라는 점
  • 코드 작성하면서 상의할 부분은 바로바로 했던 점
  • 오버커뮤니케이션으로 서로가 어떤 업무를 하고 있는지 바로 알 수 있었던 점
  • Pull Request에 코드 리뷰를 꼼꼼하고 상세하게 했던 점
  • 깃허브 이슈탭을 활용하여 발견한 에러나 이슈를 올려놓고 차근차근 해결한 점
  • 주말에 급하게 회의할 때도 모두 참석했던 점
  • 유용한 단축키나 명령어를 공유했던 점 ㅋㅋㅋ

 

 

 

🥲 아쉬웠던 점


아쉬웠던 점은...딱히 없는 것 같습니다..

굳이 꼽자면 프로젝트 기간이 너무 짧다는 것이 아쉬운 점이라 할 수 있겠네요.

저희가 기간은 생각하지 않고 팀원 모두가 만들고 싶은 서비스를 기획하다보니 기능이 너무 많아진 것 같긴합니다만ㅋㅋㅋ

그래도 기능이 복잡한 만큼 해결해나가는 재미가 있었습니다.

앞으로 수료 이후에도 같이 완성하기로 해서 앞으로도 기대됩니다.

 

 

 

배운 점


프로젝트 동안 여러 문제를 만났고 그 문제들이 저와 팀원들을 많이 성장시켜 주었습니다.

  • Spring Security에서 CORS 해결을 위해 AllowedOriginPattern을 사용할거면 정규표현식을 올바르게 써야한다는 점
  • DB에서 식별관계로 매핑하고 PK 2개이상 설정하는 방법
  • Github secrets는 프로젝트 내부의 yml 설정 코드등에 자동으로 적용이 안된다는 점...
  • 깃허브 명령어
  • 트러블 슈팅은 그때그때 기록하지 않으면 까먹는다는 점
  • DB 테이블에서 겹치는 테이블은 하나의 테이블로 묶는게 효율적이라는 점
  • 협업할때는 메서드 이름을 통일하고 시작하는게 좋다는 점
  • 다른 엔티티의 서비스의 호출이 필요할 때는 서비스단에서 주입하는것이 좋다는 점
  • 웹사이트 도메인을 구매하고 aws에 연결하는 모든 과정
  • 로드밸런서의 사용법
  • 웹사이트에 이미지를 업로드하고 브라우저에서 보여지는 플로우
  • EC2 건강하지 않다는 오류 해결법
  • CORS 해결하는 다양한 방법..

 

 

 

 

메인 프로젝트를 마치며...


저희의 서비스 개발은 아직 끝나지 않았지만 코드스테이츠에서 정한 기간이 끝나 다같이 회고를 작성하고 있습니다.

프로젝트 시작할 때 기대와 설렘을 가득 가지고 시작했는데 기대보다도 더 즐겁고 유익한 시간이었고 다사다난했지만 그만큼 즐거웠습니다.

팀원분들과 즐겁게 코딩하면서 많이 배워 감사한 시간들이었습니다.

프로젝트기획 단계 중간에 디자이너도 합류하고 다들 참 열심히 달려왔던것 같네요.

가끔은 밤을 새가고 가끔은 하루종일 회의를 하기도 하면서 다들 열정이 넘쳐서 즐거웠습니다.

발표영상 찍은 후의 우리의 모습

다들 열심히 한만큼 녹초가 되어있었지만 그래도 즐겁고 뿌듯했습니다.

 

 

 

 

부족한 팀장이었지만 다들 열심히 잘 해주셔서 너무나 감사합니다. 

팀원분들께 압도적 감사!!!!

 

 

배포 링크 : https://catvillage.shop 

 

Cat Village

 

catvillage.shop

깃허브 링크 : https://github.com/codestates-seb/seb39_main_059

 

GitHub - codestates-seb/seb39_main_059: 🐱Community For Cat Lover! 냥빌리지🐱

🐱Community For Cat Lover! 냥빌리지🐱. Contribute to codestates-seb/seb39_main_059 development by creating an account on GitHub.

github.com

 

 

 

 

 

메인 프로젝트를 진행하면서 ERD를 사용해서 DB를 설계할 때 PK로 id 칼럼을 따로 만들 필요가 없어서 외래키 2개를 묶어서 테이블의 PK로 사용하도록 하는 테이블이 몇가지 있었다.

보통의 테이블에선 PK로 id 칼럼을 만들지만 '다대다' 연관관계에 있는 테이블은 중간에 테이블을 생성해서 '1대다', '다대1' 로 나눠주어야 서로 참조할 수 있기 때문에 중간에 데이터는 안들어가고 두 테이블의 PK만 가지고 있는 테이블이 생겼다.

메인 프로젝트(냥빌리지) ERD 일부

위와 같이 FK 2개만 가지고 테이블을 생성하였기 때문에 두개의 FK를 테이블의 PK로 사용한다.

DB에 테이블 생성할 때는 테이블을 먼저 다 생성한 다음 PK 제약조건으로 'TagToBoard'의 'boardId'와 'boardTagId'를 PK로 만든 후 외래키 제약 조건을 걸어주면 설정이 완료 된다!

 

그런데 Spring에서는 Entity에 PK를 하나밖에 지정하지 못하기 때문에 buid error가 발생한다.

해결 방법으로는 크게 3가지가 있는데 'Spring 복합 PK' 라고 검색하면 자세한 글들이 많다.

그중에 우리팀은 @IdClass 를 이용한 방법을 사용하였다.

사실 이 방법이 가장 간단한것 같다.

 

먼저 PK로 사용할 변수들에 @Id 애너테이션을 붙여준다. (2개 사용하므로 2개에 각각 붙여준다.)

먼저 Id를 묶어줄 Id 클래스를 만들어야 한다. 주로 TagToBoardId 이런식으로 ~Id 라고 지으면 나중에 보기 편하다!

그리고 Serializable을 구현한 구현체로 만들어 준다.

 

그리고 Id 클래스 위에 3가지 애너테이션을 꼭 추가해주어야 한다.

1. @Data

2. @NoArgsConstructor

3. @AllArgsConstructor

Id 클래스의 필드로 TagToBoard의 Id 필드 이름을 그대로 넣어준다.

이때 DB에 실제 저장되는 타입으로 넣어주어야 한다!!

예를 들어 JPA 를 사용하기 때문에 클래스 객체로 참조하였지만 실제 PK에 값은 boardId와 boardTagId값인 Long타입으로 들어갈 거기 때문에 Long 으로 해야한다. 변수명은 동일하게 지어준다.

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TagToBoardId implements Serializable {
    private Long board;
    private Long boardTag;
}

 

그리고 다시 TagToBoard 로 돌아와서 클래스 위에 @IdClass(TagToBoardId.class) 애너테이션을 추가해준다.

@Getter
@Entity(name = "TAG_TO_BOARD")
@NoArgsConstructor
@IdClass(TagToBoardId.class)
public class TagToBoard {
    @Id
    @ManyToOne
    @JoinColumn(name = "BOARD_ID")
    @JsonBackReference
    private Board board;

    @Id
    @ManyToOne
    @JoinColumn(name = "BOARD_TAG_ID")
    @JsonBackReference
    private BoardTag boardTag;
}

이렇게 묶어주면 오류 없이 복합 PK를 사용할 수 있다.

그리고 repository를 JpaRepository를 extends 해서 사용할 경우 id에 보통은 Long 등 숫자 타입이 들어가는데 대신에 생성한 id 클래스명(TagToBoardId)을 작성해주면 된다!

public interface TagToBoardRepository extends JpaRepository<TagToBoard, TagToBoardId> {
}

 

 

 

테이블명이나 칼럼명이 MariaDB 예약어에 등록되어 있으면 생성할 때 오류가 발생한다.

▷ MariaDB 예약어 목록

 

 

ddl auto를 사용하지 않고 DB에서 직접 테이블을 생성하다보면 제약조건이 꼬이거나 하는 문제가 발생할 수 있다.

이때 제약조건을 확인할 수 있는 쿼리문이 있어서 같이 올려놓는다.

모든 제약조건 확인하는 쿼리

SELECT *
FROM information_schema.table_constraints;

 

 

 

'DATABASE' 카테고리의 다른 글

[MySQL] 데이터베이스 테이블 생성 후 column 속성 변경  (0) 2022.09.06
[MySQL] 유용한 쿼리문  (2) 2022.09.03

 

 

 

예전에 TIL 에서 작성했던 글 중에 AWS VPC와 subnet에 관해 정리한 글이 있어서 AWS 카테고리에 다시 정리해서 올린다. 🙂

 

 

 

 

 

🍹 VPC 란?

 

VPC : Vitual Private Cloud 서비스

클라우드 내 private 공간을 제공해서 클라우드를 public과 private 영역으로 논리적으로 분리할 수 있게 해준다.

 

 

 

🍹 VPC 구성 요소와 주요 용어

 

 

🍺 IP Address

 

IP는 컴퓨터 네트워크에서 장치들이 서로를 식별하기 위해 사용하는 특수한 번호이다.

OSI의 3계층인 네트워크 계층에 해당된다.

IPv4, IPv6로 나뉘어 있으며 혼용하여 사용하고 있다.

IPv4는 각 8비트, IPv6는 각 32비트로 4개의 옥텟으로 이루어져 있다.

네트워크 주소와 호스트 주소로 나뉘어 진다.

 

🌱 네트워크 주소 : 호스트들을 모은 네트워크를 지칭하는 주소이다. 네트워크 주소가 동일한 네트워크를 로컬 네트워크라고 한다.

🌱 호스트 주소 : 하나의 네트워크 내에 존재하는 호스트를 구분하기 위한 주소

 

네트워크 주소와 호스트 주소를 나누는 경계점이 고정되어 있지 않다.

IP주소는 네트워크 주소와 호스트 주소를 나누는 경계점에 따라 클래스(Class)를 나눈다.

호스트 IP 개수에 따라 네트워크의 크기를 다르게 할당할 수 있다.

클래스는 총 5가지(A, B, C, D, E)로 나뉘어져 있다.

D와 E 클래스는 멀티캐스트용, 연구 개발을 위한 예약 IP라서 보통은 사용하지 않는다.

 

A, B, C 클래스의 맨 앞자리 숫자만 보면 무슨 클래스인지 식별이 가능하다.

클래스풀(Classful) 방식이라고 부른다.

 

 

 

🍺 CIDR(Classless inter-domain routing)

 

클래스 없는 도메인 간 라우팅 기법으로 사이더라고 불린다.

현재 국제 표준의 IP 주소 할당 방법이다.

클래스풀 방식을 대체한 방식이다.

 

기존에는 클래스에 따라 정해진 Network Address와 Host Address를 사용했지만 CIDR은 원하는 블록만큼 Network Address를 지정할 수 있다.

AWS의 VPC는 CIDR 방식이다.

 

ex) 172.16.0.0/24

-> /24 이 서브넷 블록, 네트워크 주소에 앞에서부터 24비트인 172.16.0 부분만 할당한다는 의미이다.

 

AWS VPC에서는 /16 블록을 사용하도록 권장하고 있다.

※ 추가 자료) AWS VPC 기본 및 연결 옵션

 

 

 

🍺 서브넷(Subnet)

 

Subnetwork의 줄임말이다.

IP 네트워크의 논리적 하위 부분을 가리킴

서브넷을 통해 하나의 네트워크를 여러 개로 나눌 수 있음

VPC를 사용하면 필요에 따라 다양한 서브넷을 생성할 수 있음

  • 퍼블릭 서브넷: 인터넷을 통해 연결할 수 있는 서브넷
  • 프라이빗 서브넷: 인터넷을 연결하지 않고, 보안을 유지하는 배타적인 서브넷
  • VPN only 서브넷 : 기업 데이터 센터와 VPC를 연결하는 서브넷

 

AWS VPC

 

 

서브넷은 VPC의 CIDR 블록을 이용해 정의된다.

최소 크기의 서브넷은 /28 이다.

서브넷은 AZ(Availability Zone, 가용 영역)당 최소 하나를 사용할 수 있고, 여러 개의 AZ에 연결되는 서브넷은 만들 수 없다.

 

※ 참고) AWS 예약된 서브넷

AWS가 확보한 서브넷 중 처음 네 개와 마지막 IP 주소는 인터넷 네트워킹을 위해 예약되어 있다. 예를 들어 10.0.0.0/24 체계의 CIDR 블록이 있는 서브넷에서 10.0.0.0, 10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.255 는 IP 주소가 예약되어 있다.

 

 

🍺 라우팅 테이블(Routing Table)

 

트래픽의 전송 방향을 결정하는 라우트와 관련된 규칙을 담은 테이블이다.

목적지까지 최적의 경로로 데이터 패킷을 전송하기 위한 모든 정보를 담고 있다.

모든 서브넷은 라우팅 테이블을 가진다.

하나의 테이블 규칙을 여러 서브넷에 연결할 수 있다.

서브넷 생성 후 별도의 라우팅 테이블을 생성하지 않으면 클라우드가 자동으로 VPC의 메인 라우팅 테이블과 연결한다.

 

 

 

 

 

'AWS' 카테고리의 다른 글

AWS의 EC2 인스턴스 종료? 중지?  (0) 2022.09.03

 

 

 

프로젝트 처음으로 해보면서 Github의 다양한 기능을 공부하게 된 것 같다..

깃허브로 협업을 하다보면 Pull Request에서 머지 전략을 선택할 수 있도록 되있는 것을 볼 수 있다.

깃허브에서 pull request를 merge 할 때 총 3가지의 방법중 선택할 수 있다.

머지 전략은 각 브랜치 전략에 맞춰서 사용하면 된다.

 

 

Merge Commit

흔히 잘 알고있는 merge 전략이다.

기존 브랜치의 모든 commit을 그대로 붙이고 merge commit 까지 남기는 방식이다.

A 브랜치에서 B 브랜치로 PR을 해서 Merge Commit 전략으로 Merge 한다고 생각해보자.

그러면 아래와 같이 commit이 남을 것이다.

A branch : commit1 -> commit2 -> commit3

B branch : commit1 -> commit2 -> commit3 -> Merge pull request #번호 ~~

이런 식으로 merge된 B브랜치에 A브랜치의 commit 내역 + merge 됐다는 commit 까지 같이 남는다.

 

 

Squash and Merge

이건 commit을 모두 하나로 뭉쳐서 commit 1개만 남기는 전략이다.

예를 들어 A 브랜치에서 B 브랜치로 올린 PR을 'Squash and Merge' 전략으로 merge 한다고 하면 아래와 같이 남는다.

A branch : commit1 -> commit2 -> commit3

B branch : commit

A 브랜치의 커밋 내용들을 모두 하나로 뭉쳐서 commit을 하나만 남기는 전략이다.

커밋의 타이틀에 원하는 커밋 내용을 쓸 수 있고 body에는 자동으로 commit 내역이 붙는다.

Squash Merge 후 commit 내용

'Update README.md' 라는 내용의 커밋을 3개 남긴 후에 Squash Merge한 내용이다.

커밋 내용은 원하는대로 바꿔쓸 수 있다.

그리고 merge 커밋의 body에 자동으로 병합 전 커밋 내용이 나와있는 것을 볼 수 있다.

 

 

Rebase and Merge

이건 아예 rebase 하는 전략이다.

브랜치 pull 이나 push할 때 config에서 rebase를 설정하거나 옵션으로 rebase를 줄 수 있다.

Rebase and merge 전략도 그 rebase와 비슷하다.

이 전략을 이용하면 merge commit 없이 commit을 그대로 붙여놓을 수 있다.

이번에도 A 브랜치와 B 브랜치로 예시를 들어봤다.

A branch : commit1 -> commit2 -> commit3

B branch : commit1 -> commit2 -> commit3

이런식으로 A 브랜치가 B 브랜치의 부모 브랜치가 되고 merge commit이 붙지 않는 것을 볼 수 있다.

 

 

 

우리 팀에서는 feature 브랜치에서 dev 브랜치로 merge 할 때는 되도록이면 commit을 다 남겨놓기 위해 'Merge Commit'을 사용하기로 했고

dev에서 main 브랜치로 merge 할 때는 'Squash and Merge'를 사용하기로 했다.

어떻게 merge commit을 남길 것인지에 따라서 merge 전략을 골라서 사용하면 된다!

 

 

 

읽어주셔서 감사합니다. 😍

오개념에 대한 지적은 언제나 환영입니다! 

'GIT' 카테고리의 다른 글

[GIT] 협업할 때 유용한 명령어 (CLI 명령어)  (0) 2022.09.05

 

 

 

짧았던 프리 프로젝트 기간이 어제부로 끝났다. 우리는 인원 부족으로 프론트엔드 팀원 한 분이 들어오면서 프리 프로젝트가 끝났다.

팀원이 늘어나면서 팀번호가 바껴버려서 팀 번호 럭키 7을 잃었다...🥲

메인 프로젝트 첫날은 OT형식으로 진행되었고 프리 프로젝트에 대한 회고 시간을 가졌다.

 

 

⚡️ 프리 프로젝트 진행하면서 좋았던 부분?

 

프리 프로젝트 진행하면서 좋았던 것, 배웠던 것 참 많았다.

그 중에 가장 좋았던 건 팀원들 이다. 난 운이 정말 좋게도 좋은 사람들만 만나서 팀 내에 커뮤니케이션 문제, 팀원간의 이슈는 없었고 서로에게서 많이 배우고 자극 받는 좋은 관계를 만들 수 있었다. 서로 대화할 때도 쿠션어를 사용하고 비난이나 불만을 표출하지 않고 잘 따라와주었다. 사실 내가 팀장으로 크게 한 일은 없었고 전체적인 일정 관리나 회의 진행하고 의견 하나로 모은 정도가 다였다.

다들 긍정적인 덕분에 매번 즐거웠고 같이 하면서 좋은 에너지를 많이 받을 수 있었다. ⚡️

프리 프로젝트를 하면서 특별한 일이 없으면 매일 2번의 회의를 했다.

우리가 짧은 시간과 적은 인원(3명)으로 사용자 요구사항 명세서(SRS)에 정의한 모든 기능을 모두 완성하진 못했지만 훈훈하게 우리의 작품을 뿌듯해하며 마무리를 하였다.

모두가 일단 가장 좋았다고 뽑은건 팀 분위기, 커뮤니케이션 부분이었다.

 

개인적인 좋았던 부분

  • 기술적으로 이슈들은 항상 바로바로 연락을 하고 깃허브 이슈 탭을 적극 활용한 점이 좋았습니다. 😄
  • 유용한 기능은 혼자 알지 않고 서로 공유해서 너무 좋았습니다.
  • 논의, 토론을 하면서 서로를 존중하고 기분 나쁘지 않게 대화하는 점이 좋았습니다.
  • 팀원분들 통해서 몰랐던 부분이나 헷갈렸던 부분들 많이 배울 수 있었습니다.
  • 열정 넘치고 긍정에너지 넘치는 팀원들을 통해 에너지를 많이 얻을 수 있었습니다.
  • 모두가 적극적으로 의견을 제시해서 팀 전체가 계속 더 좋은 방향으로 나아갈 수 있어서 좋았습니다.
  • 부족한 팀장을 잘 도와주셔서 부담을 많이 덜수 있었습니다. 다들 너무 고맙습니다. 😻

 

개발에 있어서 전체적으로 만족했던 부분

  • 문서 작성을 자세하게 했던 부분 (API 명세서, 화면정의서)
  • 설계 단계에서 다같이 상의한 점
  • 프론트엔드와 백엔드가 각자 코딩하고 모여서 합쳤을 때 동작한 것
  • 서버 단에서 각자 도메인 별로 나눠서 코딩하고 한번의 run 없이 합쳤는데 큰 문제 없이 합쳐진 것
  • 배포할 때 프론트엔드 백엔드 같이 에러를 확인하고 해결해나간 점
  • 배포에 AWS를 사용하면서 aws의 전체적인 흐름과 사용법을 익힐 수 있었던 부분

 

 

☄️ 팀 문화와 팀 룰

 

팀원들과 팀 문화와 팀 룰을 초반부터 정해서 지켜갔는데 이게 팀 분위기를 형성한 데 도움이 된 것 같다. 특히 오버커뮤니케이션을 지향했기 때문에 우리의 슬랙 채널은 항상 뜨거웠고 덕분에 서로 성장하는데 도움이 될 수 있었다.



팀문화 페이지

 

프로젝트 룰 페이지

 

그리고 프리 프로젝트를 마무리하면서 회고 날 전에 우리 팀에서는 이미 팀룰과 팀 문화에 있어서 KPT를 진행하였다. 모두들 팀 룰과 팀 문화에 만족하고 있었고 프리 기간동안 입증이 끝난 상태였다.

팀 룰에서 commit과 git branch 그리고 코딩할 때 코드의 통일성을 맞추기 위해서 coding convention까지 정했었는데 이게 큰 도움이 된 것 같다. 팀원들이 모두 프로그래밍 룰을 잘 지켜준 덕분에 git issue창과 branch가 깔끔하게 정리되었다. 추가로 깃에서 Pull Request의 템플릿을 만들어 기본으로 지정할 수 있는데 이게 PR 글들을 깔끔하게 정리해줬고 PR을 들어가면 자세하게 정리되어 있어서 커밋을 하나하나 찾아볼 필요가 없었다. 😄 깃 관리가 잘 된것 같아 아주 만족했다~

 

 

 

✨ 아쉬웠던 점과 개선할 부분

 

개인과 팀 전체로 나눠서 아쉬웠던 점과 개선할 부분도 같이 회고하였다.

 

개인적인 측면에서 아쉬웠던 부분

  • 시간적 자원과 인적 자원이 모자라서 계획한 것을 100프로 구현하지 못한 점
  • 다른 포지션과의 협업이 처음이라 개발 초반에 프론트엔드와 백엔드의 소통이 부족했던 것 같습니다.
  • 클라이언트와 서버 통신을 미리 테스트 하지 않고 개발이 어느정도 된 상태에서 하니까 급작스럽게 처음보는 문제들을 마주쳤습니다.
  • 시간이 부족하여 기술적인 구현 연습을 충분히 못했습니다..
  • 에러에 대해서 깃헙 이슈에만 남겨놓고 회고로 남겨놓진 못했습니다.
  • 깃헙 액션 사용 못해서 배포 자동화 테스트 못해본것

 

개인적인 측면에서 개선할 부분

  • 다른 포지션 팀원들과의 소통에 더 적극적으로 임할 필요가 있습니다.
  • 개발 초기 단계에 개발 서버에서 클라이언트와 서버 통신이 정상적으로 되는지 확인하면 좋을 것 같습니다!
  • 에러 일지 등 회고 작성하면서 할 필요가 있겠습니다!! 📝

 

팀원 모두가 아쉬웠던 부분

  • 처음 설계하는 거라 빠졌던 내용이 많아서 중간중간 수정이 잦았습니다.
  • API 명세서를 구글 스프레드 시트로만 작성하고 다른 좋은 툴을 활용하지 못했던 부분
  • (백엔드) 서버에 Spring Security를 적용하지 못한 점
  • (백엔드) 테스트를 구현해보지 못한 점
  • 배포 서버 테스트를 미리 하지 않은 부분 → 첫 배포에서 많은 오류를 겪었습니다..
  • 개발 서버와 배포 서버를 분리하지 않고 동일하게 사용한 부분

 

팀원 모두가 개선해야겠다고 생각한 부분

  • 메인 프로젝트 때는 API 명세서를 Swagger로 정리하자
  • (백엔드) 상속 및 확장 관계에 대해 한번 더 생각해보고 설계 및 개발하기(클래스 관계도 다이어그램 미리 그려보기!)
  • (백엔드) 처음부터 시큐리티를 적용하여 프로젝트 진행하기
  • 중간중간 build해서 체크가 필요합니다.
  • 장인 정신 좋지만 시간을 고려할 필요가 있습니다.
  • 배포 서버 테스트를 개발 초기 단계에 미리 해보면 좋겠습니다.
  • 시간적으로 가능하면 배포 서버와 개발 서버를 분리해 봅시다. → AWS 프리 티어 계정 2개로 분리
  • 회고... 기록을 너무 안남겼다... 에러를 잘 적어놓자..

 

 

 

모두가 즐거우면서도 유익한 회고 시간이었다.

팀원 모두가 메인 프로젝트 때는 프리 프로젝트 때보다 더 불태우려는 의지를 보였다! 🔥🔥🔥

우리는 프로젝트에 열정을 담아서 팀명을 24/7로 지었다. ㅋㅋㅋ (블랙기업이냐고 얘기가 나왔다..ㅋㅋㅋ)

 

 

 

 

 

 

 

이미 테이블을 생성한 이후 칼럼의 속성을 변경하려는 경우 문법이 헷갈릴 수 있다.

그래서 테이블 생성 후에 칼럼을 추가하거나 속성 변경, 외래키 등 추가하는 문법을 정리해 놓는다.

 

☑️ MySQL에서 명령어는 대소문자 구분이 없기 때문에 소문자로 작성해도 된다.

 

 

🐙 column 추가하기

ALTER TABLE [table명] ADD [column명] [자료형];

 

 

🐙 column에 not null 속성 부여하기

ALTER TABLE [table명] MODIFY [column명] [자료형] NOT NULL;

☑️ 'NULL' 로 변경하고 싶다면 'NOT NULL' 부분을 지우면 된다.

☑️ 자료형을 바꾸고 싶다면 [자료형]에 바꾸고 싶은 자료형을 입력한다.

 

 

🐙 column에 unique 속성 부여하기(Unique key)

ALTER TABLE [table명] MODIFY [column명] [자료형] UNIQUE;

☑️ MySQL에는 Unique key(UNI), Primary key(PRI), Multiple key(MUL) 이렇게 총 3종류의 key가 있는데 unique 키는 중복성이 허용되지 않지만 null에 대한 허용이 가능하다. unique 속성을 부여하면 table key에 'UNI'라고 적혀있다.

☑️ 만약에 key가 uni, pri, mul 중 여러개에 해당된다면 PRI < UNI < MUL 순의 우선순위로 출력된다.

 

 

🐙 column 이름 변경

ALTER TABLE [table명] CHANGE [기존column명] [변경할column명] [자료형];

 

 

🐙 column 삭제하기

ALTER TABLE [table명] DROP COLUMN [column명];

 

 

🐙 column default 값 변경하기

ALTER TABLE [table명] COLUMN [column명] SET DEFAULT [디폴트값];

 

 

🐙 column에 값 자동 증가(auto_increment) 설정하기

ALTER TABLE [table명] MODIFY [column명] [자료형] AUTO_INCREMENT;

 

 

🐙 column에 auto_increment 값 초기화 하기

ALTER TABLE [table명] AUTO_INCREMENT=[초기화 후 시작숫자];

 

 

🐙 column 순서 변경하기

ALTER TABLE [talbe명] MODIFY [순서바꿀column명] [자료형] AFTER [앞에오는column명];

 

 

🐙 column에 외래키(FK) 부여하기

ALTER TABLE [table명] ADD FOREIGN KEY([column명]) REFERENCES [참조table명]([참조column명]);
ALTER TABLE USER ADD FOREIGN KEY(ORDER_ID) REFERENCES ORDER;

// 제약조건 부여하기
ALTER TABLE [table명] ADD CONSTRAINT [제약조건명] FOREIGN KEY([column명])
REFERENCES [참조table명]([참조column명]);

☑️ 두번째 줄에 작성한 것과 같이 해당 테이블에 PK가 하나밖에 없는 경우는 참조칼럼은 생략가능하다.

☑️ MySQL 결과에선 FK는 'MUL'로 나온다. (MUL은 보통은 외래키이다. 논유니크 인덱스(중복 가능)의 첫번째 칼럼을 나타낸다.)

 

 

 

 

'DATABASE' 카테고리의 다른 글

[MariaDB/MySQL] JPA Foreign Key 2개를 Primary Key로 사용하기  (0) 2022.10.01
[MySQL] 유용한 쿼리문  (2) 2022.09.03
1 2 3 4 5 ··· 11