BEYOND SW 캠프 15기 / 6-7주차 회고

Wish·2025년 3월 9일

BEYOND SW15

목록 보기
7/12
post-thumbnail

TIL(This week I Learned)

  1. 객체 지향 프로그래밍의 특징
  2. 입출력 클래스와 Collection 클래스 활용
  3. 객체지향 설계의 원칙과 객체 디자인 패턴
  4. 자료구조와 알고리즘

Facts

1. 백준 코딩테스트 스터디

class1에 이어서 class2 문제풀이를 시작했다. 문제량이 조금 늘기도 했지만 확실히 난이도가 올라간 것 같다. 이번 클래스 문제에 통계학과 관련된 문제가 있었다. 학교 다닐 때도 통계학 시간에 머리 싸매고 있었던 것 같은데 코드로 구현하려니까 막막했다. 열심히 푼다고 풀어보고 답이 안 나와서 다른 사람들 풀이를 찾아보기도 했는데, 잘 이해가 되지는 않았다.

2. 자바 콘솔 프로그램 개발

Java Programming 학습을 마무리하면서 컬렉션과 파일 입출력을 이용한 종합 실습을 진행했다. 7주차에 컨디션이 많이 안 좋았어서 결과물이 사실 많이 아쉽기는 하다. 다른 분들 코드를 보니 아이디어가 기발하신 분들도 있고 또 내가 미처 생각하지 못했던 부분들 까지 세세하게 신경쓰신 분들도 많았다. 나는 영화예매 프로세스를 개발해봤는데, 기능을 구현해보고 배운 클래스 메소드나 api를 활용해본다는 생각에만 급급했던 것 같다.

3. 알고리즘 관련 코딩 테스트 문제 풀이

알고리즘 강의 한 파트가 마무리 될 때마다 강사님이 학습 내용과 관련된 코딩 테스트 문제를 내주셨다. JUnit을 활용해서 문제를 풀다보니 기존에 백준 문제를 푸는 것과는 약간 다른 방향으로 풀어야 한다고 생각했는데, 메소드 파라미터로 전달 받은 스트링도 StringReader과 BufferedReader 스트림으로 읽어서 처리할 수 있다는 것을 배웠다.

Findings

heap과 관련된 문제를 푸는데, 백준에서는 통과된 코드가 JUnit 테스트 결과로는 실패가 나와서 당혹스러웠다. 그것도 첫 번째 테스트는 통과하는데, 두 번째 테스트만 실패로 나와서 더 머리가 아팠다.

문제) 세계적인 도둑 상덕이는 보석점을 털기로 결심했다. 상덕이가 털 보석점에는 보석이 총 N개 있다. 각 보석은 무게 Mi와 가격 Vi를 가지고 있다. 상덕이는 가방을 K개 가지고 있고, 각 가방에 담을 수 있는 최대 무게는 Ci이다. 가방에는 최대 한 개의 보석만 넣을 수 있다. 상덕이가 훔칠 수 있는 보석의 최대 가격을 구하는 프로그램을 작성하시오.
실패 코드 1)
1. 각각의 보석에 대한 무게와 값을 저장해야 하므로, 클래스 내부에 jewel 클래스를 선언한다. 이 때 보석의 무게가 같다면, 더 비싼 보석을 담을 수 있는 정렬이 필요하다. Comparable 인스턴스를 implements 하고, 제네릭을 통해 객체 타입을 jewel로 제한한다.
2. JUnit 테스트에서 호출시 넘겨준 파라미터 값을 SpringReader 스트림을 통해 받고 빠른 입출력을 위해 BufferedReader 버퍼 스트림을 사용한다.
3. 각 보석의 정보를 저장할 jewel[] 배열jewels와, 보석을 담을 가방 배열 int[] bags 를 입력받은 크기로 생성한다.
4. 보석의 수 만큼 반복하면서 jewels에 각 보석의 가격과 무게를 저장한 jewel 객체를 담아 오름차순으로 정렬한다.
5. 가방의 수 만큼 반복하면서 각 가방에 담을 수 있는 보석의 최대 무게를 bags에 담아 오름차순으로 정렬한다.
6. 우선순위(보석의 무게)에 따라 값을 정렬하기 위해 PriorityQueue를 사용한다. 무거울 수록 우선순위가 높아지므로 PriorityQueue 객체 생성 시 내림차순 정렬 reverseOrder();를 명시한다.
7. 가방의 수만큼 for문을 반복하며 내부에 while문을 선언해 가방에 담을 수 있는 최대 무게와 비교해 그 무게가 작거나 같은 보석의 가격을 queue에 반복해서 담는다. 각 가방과 보석들의 무게 비교가 완료될 때마다 queue에 담긴 보석들의 가격 중 가장 비싼 보석의 가격을 poll()해 return할 결과 값에 담는다.
public class Practice2 {

    static int N, K;
    static long result = 0;
    static int[] bags;
    static jewel[] jewels;

    static class jewel implements Comparable<jewel> {

        int value;
        int weight;

        public jewel(int value, int weight) {
            this.value = value;
            this.weight = weight;
        }

        @Override
        public int compareTo(jewel o) {
            if(this.weight == o.weight) return o.value - this.value;
            return this.weight - o.weight;
        }
    }

    public long solution(String input) throws Exception{

        BufferedReader br = new BufferedReader(new StringReader(input));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken()); // 보석 수
        K = Integer.parseInt(st.nextToken()); // 가방 수
        jewels = new jewel[N];
        bags = new int[K];

        for(int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            int M = Integer.parseInt(st.nextToken());
            int V = Integer.parseInt(st.nextToken());
            jewels[i] = new jewel(V, M);
        }

        for(int i = 0; i < K; i++) {
            st = new StringTokenizer(br.readLine());
            int C = Integer.parseInt(st.nextToken());
            bags[i] = C;
        }

        Arrays.sort(jewels);
        Arrays.sort(bags);

        PriorityQueue<Integer> queue = new PriorityQueue<>(Comparator.reverseOrder());

        for(int i =0,j = 0; i < K; i++) {
            while(j < N) {
                if(bags[i] < jewels[j].weight) break;
                queue.add(jewels[j++].value);
            }
            if(!queue.isEmpty()) result += queue.poll();
        }
        return result;
    }
}

위와 같은 방법으로 짠 코드를 백준에서 테스트 했을 때 아무런 문제가 없었는데, 왜 JUnit 테스트에서는 문제가 발생했을까. 예전에 모 드라마에서 이런 대사가 나온 적이 있다. "숫자는 거짓말을 하지 않는다.".

1차 테스트에서 나온 결과값은 10, 그리고 2차 테스트를 성공하려면 163이라는 값이 반환되어야 한다. 그런데 나는 173이 나왔다. 같은 코드를 돌렸는데, 어떻게 다른 테스트 결과를 도출할 수 있는걸까. 특별히 에러가 발생하지도 않아서 정말 오리무중이었는데, 강사님과 천천히 대화를 하면서 생각해보니 10이라는 값이 추가적으로 발생할 수 있는 부분은 딱 하나였다. 1차 테스트에서 저장된 result 값 10.

그렇다. JUnit 테스트를 할 때 테스트 프로세스는 1차 테스트 후 종료되고 2차 테스트가 진행되는 것이 아니라 모든 테스트가 완료된 후에 하나의 프로세스가 종료된다. 그러니 당연히 비동기적으로 처리되는 내 전역변수 result에는 1차 테스트 결과 값이 남아있을 수 밖에 없었다. 문제를 인식하고 전역에 처리해뒀던 변수들을 solution 메소드 내부에 지역변수로 선언했더니 2차 테스트도 무사히 잘 통과되었다. 기초적인 실수를 하다니 아직 갈 길이 먼 것 같다.

Future

작은 변수 하나일 지라도 결과에는 큰 변수가 될 수 있다는 것을 깨달았다. 앞으로 코딩을 할 때 객체나 변수의 선언 위치에 대해서도 세심하게 신경쓰면서 작업해야겠다. 또 단순히 에러코드만 볼 게 아니라 테스트 결과로 도출된 값에 대해서도 분석하는 습관을 들여야 겠다.

For ME🍀

건강 잘 챙기기

profile
원하는 것을 이뤄가는 중 🍀

1개의 댓글

comment-user-thumbnail
2025년 3월 10일

건강 잘 챙기셔요 :)
오늘은 컨디션 좋아보이십니다 :) 👍

답글 달기