티스토리 뷰

프리코스

많이 기대하고 기다리던 우아한테크코스 프리코스가 10월 26일부터 시작되었습니다. 4기까지만 하더라도 프리코스에 참여하기 위해서는 온라인 코딩 테스트를 통과해야만 했으나, 5기부터 프리코스를 최대한 많은 사람이 겪어볼 수 있게 하도록 온라인 코딩 테스트를 진행하지 않았습니다.

 

프리코스는 위의 설명과 같이 우아한테크코스 본 과정에 들어가기 전 매주 미션을 하나씩 구현하고 제출하는 방식으로 진행됩니다. 이전 기수들의 프리코스 과제를 해보았을 때 요구사항 분석을 통해 해결해야 할 기능을 정리하고 기능을 구현하는 과정에서 코드에 대해 많은 고민할 수 있었기에 이번 프리코스에는 어떤 과제가 나올지 궁금하였습니다.

1주차

진행 방식

  • 미션은 기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항 세 가지로 구성되어 있다.
  • 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.
  • 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.

진행 방식을 읽어보며 재밌었던 내용은 '기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.' 였습니다. 보통 일반적인 과제라면 정해지지 않은 규칙에 대해서는 제한을 두곤 하는데 자신의 판단에 맡긴 자유로운 학습을 추구하는 점이 매력적으로 느껴졌습니다.

들어가기 전

프리코스를 진행하기 전 우아한테크코스 클린코드 원칙에 대해 살펴보고 정리해보았습니다.

  • 자바 코드 컨벤션을 지키면서 프로그래밍했는가?
  • 한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
  • else 예약어를 쓰지 않았는가?
  • 모든 원시값과 문자열을 포장했는가?
  • 콜렉션에 대해 일급 콜렉션을 적용했는가?
  • 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
  • getter/setter 없이 구현했는가?
  • 메소드의 인자 수를 제한했는가?
  • 코드 한 줄에 점(.)을 하나만 허용했는가?
  • 메소드가 한가지 일만 담당하도록 구현했는가?
  • 클래스를 작게 유지하기 위해 노력했는가?

위의 원칙들을 최대한 지키면서 코드를 작성하기 위해 노력해보았습니다. 요구사항 분석, 구현 기능 및 역할 설정, Solution 작성 순으로 문제를 해결해나갔습니다. 객체지향의 사실과 오해를 읽으며 학습한 내용을 바탕으로 메시지를 정하고 객체에 책임을 부여해보았습니다. Solution 부분에서는 메인 메서드만 작성해두었기에 자세한 코드는 아래에서 확인할 수 있습니다.

 

GitHub - woo-chang/java-onboarding: 온보딩 미션을 진행하는 저장소

온보딩 미션을 진행하는 저장소. Contribute to woo-chang/java-onboarding development by creating an account on GitHub.

github.com

문제 1

요구 사항

포비와 크롱이 페이지 번호가 1부터 시작되는 400 페이지의 책을 주웠다. 책을 살펴보니 왼쪽 페이지는 홀수, 오른쪽 페이지는 짝수 번호이고 모든 페이지에는 번호가 적혀있었다. 책이 마음에 든 포비와 크롱은 페이지 번호 게임을 통해 게임에서 이긴 사람이 책을 갖기로 한다. 페이지 번호 게임의 규칙은 아래와 같다.

  1. 책을 임의로 펼친다.
  2. 왼쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.
  3. 오른쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.
  4. 2~3 과정에서 가장 큰 수를 본인의 점수로 한다.
  5. 점수를 비교해 가장 높은 사람이 게임의 승자가 된다.
  6. 시작 면이나 마지막 면이 나오도록 책을 펼치지 않는다.

포비와 크롱이 펼친 페이지가 들어있는 리스트/배열 pobi와 crong이 주어질 때, 포비가 이긴다면 1, 크롱이 이긴다면 2, 무승부는 0, 예외사항은 -1로 return 하도록 solution 메서드를 완성하라.

구현 기능

1. 입력에 대한 검증을 수행한다.
  - 페이지 리스트값이 존재하는지 확인한다.
  - 페이지 리스트가 정해진 범위에 속하는지 확인한다.
  - 왼쪽 페이지가 홀수이고 오른쪽 페이지가 짝수인지 확인한다.
  - 페이지 리스트가 연속적인지 확인한다.
2. 페이지 번호 목록에 대해 최댓값을 계산한다.
  - 왼쪽 페이지 번호의 각 자리 숫자를 더하거나 모두 곱해 가장 큰 수를 구한다.
  - 오른쪽 페이지 번호의 각 자리 숫자를 더하거나 모두 곱해 가장 큰 수를 구한다.
  - 가장 큰 수를 최댓값으로 결정한다.
3. 최댓값을 비교하고 알맞은 결과를 출력한다.
  - 예외 사항에 대해 `-1`을 출력한다.
  - 포비가 이긴다면 `1`을 출력한다.
  - 크롱이 이긴다면 `2`를 출력한다.
  - 무승부일 때 `0`을 출력한다.

구현 역할

Solution

public static int solution(List<Integer> pobi, List<Integer> crong) {
    if (valid.validate(pobi) && valid.validate(crong)) {
        return result.chooseWinner(calculation.getMaxPoint(pobi), calculation.getMaxPoint(crong));
    }
    return Result.EXCEPTION;
}

문제 2

요구 사항

암호문을 좋아하는 괴짜 개발자 브라운이 이번에는 중복 문자를 이용한 새로운 암호를 만들었다. 예를 들어 "browoanoommnaon"이라는 암호문은 다음과 같은 순서로 해독할 수 있다.

  1. "browoanoommnaon"
  2. "browoannaon"
  3. "browoaaon"
  4. "browoon"
  5. "brown"

임의의 문자열 cryptogram이 매개변수로 주어질 때, 연속하는 중복 문자들을 삭제한 결과를 return 하도록 solution 메서드를 완성하라.

구현 기능

1. 문자열을 차례대로 순회하며 중복되는 문자가 발생하는 지점을 찾는다.
2. 중복되는 해당 문자가 나오지 않는 지점까지 인덱스를 증가시킨다.
3. 문자열을 순회가 종료되면 중복 문자를 삭제한 결과를 반환한다.

구현 역할

Solution

public static String solution(String cryptogram) {
    return code.decode(cryptogram);
}

문제 3

요구 사항

배달이가 좋아하는 369게임을 하고자 한다. 놀이법은 1부터 숫자를 하나씩 대면서, 3, 6, 9가 들어가는 숫자는 숫자를 말하는 대신 3, 6, 9의 개수만큼 손뼉을 쳐야 한다.

 

숫자 number가 매개변수로 주어질 때, 1부터 number까지 손뼉을 몇 번 쳐야 하는지 횟수를 return 하도록 solution 메서드를 완성하라.

구현 기능

1. 숫자가 나타내는 최소 자릿수부터 최대 자릿수로 자릿수를 증가시킨다.
2. 자릿수마다 `3`, `6`, `9`의 개수를 얻는다.
3. 모든 개수의 합을 결과로 반환한다.

구현 역할

Solution

public static int solution(int number) {
    return game.start(number);
}

문제 4

요구 사항

어느 연못에 엄마 말씀을 좀처럼 듣지 않는 청개구리가 살고 있었다. 청개구리는 엄마가 하는 말은 무엇이든 반대로 말하였다.

 

엄마 말씀 word가 매개변수로 주어질 때, 아래 청개구리 사전을 참고해 반대로 변환하여 return 하도록 solution 메서드를 완성하라.

 

구현 기능

1. 문자열을 순회하며 사전에 알맞은 문자로 변환한다.
  - 알파벳 외의 문자는 변환하지 않는다.
  - 알파벳 대문자는 알파벳 대문자로 변환한다.
  - 알파벳 소문자는 알파벳 소문자로 변환한다.
2. 변환된 결과를 반환한다.

구현 역할

Solution

public static String solution(String word) {
    return converter.convert(word);
}

문제 5

요구 사항

계좌에 들어있는 돈 일부를 은행에서 출금하고자 한다. 돈 담을 지갑이 최대한 가볍도록 큰 금액의 화폐 위주로 받는다.

 

돈의 액수 money가 매개변수로 주어질 때, 오만 원권, 만 원권, 오천 원권, 천 원권, 오백원 동전, 백원 동전, 오십원 동전, 십원 동전, 일원 동전 각 몇 개로 변환되는지 금액이 큰 순서대로 리스트/배열에 담아 return 하도록 solution 메서드를 완성하라.

구현 기능

1. 큰 값의 화폐에서 작은 값의 화폐로 정렬된 목록을 생성한다.
2. 생성한 목록을 순회하며 변환되는 개수를 결과에 저장한다.
3. 금액이 큰 순서대로 리스트에 담아 반환한다.

구현 역할

Solution

public static List<Integer> solution(int money) {
    return converter.convert(money);
}

문제 6

요구 사항

우아한테크코스에서는 교육생(이하 크루) 간 소통 시 닉네임을 사용한다. 간혹 비슷한 닉네임을 정하는 경우가 있는데, 이러할 경우 소통할 때 혼란을 불러일으킬 수 있다.

 

혼란을 막기 위해 크루들의 닉네임 중 같은 글자가 연속적으로 포함 될 경우 해당 닉네임 사용을 제한하려 한다. 이를 위해 같은 글자가 연속적으로 포함되는 닉네임을 신청한 크루들에게 알려주는 시스템을 만들려고 한다.

 

신청받은 닉네임 중 같은 글자가 연속적으로 포함 되는 닉네임을 작성한 지원자의 이메일 목록을 return 하도록 solution 메서드를 완성하라.

구현 기능

1. 2글자만 같더라도 연속적이기에 신청받은 닉네임에 대해 2글자로 나눈 목록으로 변환한다.
2. 신청받은 닉네임 목록을 순회하며 2글자를 `Key`로 가지는 `Map`의 `Value`로 자신의 인덱스를 저장한다.
3. `Map`의 `Value`는 리스트 형식이기에 중복되는 문자를 가지는 닉네임의 인덱스 목록이 저장된다.
4. 최종적으로 `Map`을 순회하며 중복되는 문자를 가지는 닉네임을 가진 지원자 이메일 목록을 결과에 저장한다.
5. 이메일 목록을 오름차순으로 정렬하고 중복을 제거한 결과를 반환한다.

구현 역할

Solution

public static List<String> solution(List<List<String>> forms) {
    List<List<String>> splitNameForms = new ArrayList<>();
    for (List<String> form : forms) {
        splitNameForms.add(separator.split(form));
    }
    Map<String, Set<Integer>> indexMap = factory.generateIndexMap(splitNameForms);
    return result.generateResult(indexMap, forms);
}

문제 7

요구 사항

레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.

  • 사용자와 함께 아는 친구의 수 = 10점
  • 사용자의 타임 라인에 방문한 횟수 = 1점

사용자 아이디 user와 친구 관계 정보 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.

구현 기능

1. 친구 관계 정보를 이용해 이름이 `Key`이고, 친구들 목록을 `Value`를 가지는 친구 관계 `Map`을 생성한다.
2. 친구 관계 `Map`을 순회하며 해당 사용자의 친구가 메인 사용자 친구 목록에 속해있는 경우 해당 사용자의 점수를 증가시킨다.
3. 방문자들을 순회하며 방문자들에 대한 점수를 증가시킨다.
4. 점수순으로 정렬해서 최대 5명의 추천 사용자를 반환하도록 한다.

구현 역할

Solution

public static List<String> solution(String user, List<List<String>> friends, List<String> visitors) {
    friendShip.createFriendShip(friends);
    return recommendation.recommendUsers(user, friendShip.createRecommendedScore(user, visitors));
}

후기

객체에 대한 고민

이번 1주차 과제를 해결하면서 가장 많은 시간을 투자한 부분은 객체에 대한 고민이라고 생각합니다. 객체를 먼저 결정하지 않고 구현해야 하는 기능을 바탕으로 어떠한 메시지가 오고 가야 하는지 먼저 생각해보았습니다. 결정된 메시지는 수행해야 하는 역할로 자연스럽게 이어졌고 해당 역할을 수행할 책임을 진 객체를 쉽게 결정할 수 있었습니다.

 

아직 위의 과정이 익숙하지 않아 많은 시간이 걸리게 되었지만, 프리코스 기간 동안 꾸준히 적용해 객체에 대해 조금 더 자세히 다가가 보고자 합니다.

코드에 대한 고민

정해진 요구사항에만 동작하는 코드가 아닌 요구사항이 변경되더라도 유연하게 변경할 수 있는 코드를 작성하기 위해 고민했습니다. 이를 위해 검증에 대한 부분, 값을 변환하는 부분, 결과를 만들어내는 부분 등을 추상화하여 코드를 작성해보았습니다. Enum 또한 적극적으로 사용하려고 하였습니다.

 

문제 3번에서 3, 6, 9를 게임에서 관리하는 상수로 판단하여 Enum으로 작성해 보았습니다.

enum GameNumber {
    THREE(3), SIX(6), NINE(9);

    GameNumber(int value) {
        this.value = value;
    }

    private final int value;

    public int getValue() {
        return this.value;
    }
}

요구사항이 변경되어 2, 5, 8로 숫자가 변경되더라도 GameNumber의 추가와 삭제만 발생할 뿐 코드상의 변화는 발생하지 않습니다. 변화에 열려 있게 코드를 작성하였기에 발생한 이점이라고 생각합니다.

다음으로

1주차에서는 자바 코딩 컨벤션, 깃 커밋 메시지, 객체지향, 확장할 수 있는 코드를 코드에 적용해 보았습니다. 많이 부족하지만 남은 프리코스 기간 동안 성장해 나가고자 합니다. 2주차에는 평소 잘 사용해보지 못했던 자바의 stream에 관해 공부하고 코드에 녹여보고 싶습니다. 또한 우아한테크코스 클린코드 원칙 중 메서드에 한 가지 역할을 부여하는 것에 집중할 것 같습니다.

 

처음부터 완벽한 코드를 작성하려고 하기보다는 기능을 먼저 구현하고 리팩토링 과정을 통해 하나씩 수정하는 것이 효율이 높을 것 같아 다음 과제는 기능 구현 -> 리팩토링으로 진행해보고자 합니다.

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday