이번 과제는 마지막 미션인 만큼 시작하기 앞서 2주차 피드백과 2주차 과제에서 작성한 나의 코드 및 다른 지원자들의 코드 리뷰를 하는데 많은 시간을 투자하였습니다.
해당 리뷰에 대한 정리 자료는 다음 링크를 통해 확인하실 수 있습니다.
2주차 피드백 정리
2주차 과제 리뷰를 하며 가장 크게 놀랐던 점은 다른 사람의 코드 리뷰를 하며 있었습니다. 같은 과제를 받아서 수행하였음에도 불구하고 프리코스 미션에 MVC패턴, 싱글톤, 일급 컬렉션 등 많은 기술들을 적용하며 과제를 진행하였다는 것에 놀라며 나도 3주차 과제의 진행에는 여러 기술들을 사용해보자!는 다짐을 하며 진행하였습니다.
이번 미션은 우리가 일상생활에서 쉽게 볼 수 있는 자판기를 코드로 구현하는 미션입니다.
미션 코드의 진행 순서는 다음과 같습니다.
1. 자판기가 보유한 금액을 입력합니다.
2. 1에서 입력된 금액으로 자판기는 랜덤하게 동전을 생성한다. (보유한 동전 출력)
3. 자판기에서 판매할 상퓸 리스트를 입력합니다.
4. 음료를 구매할 투입 금액을 입력합니다.
5. 구매할 음료를 입력한다.
(위의 4,5번을 잔돈이 상품의 최저 가격보다 적거나, 모든 상품이 소진 된 경우까지 반복한다.)
6. 잔돈을 반환한다. (잔돈이 부족한 경우 반환할 수 있는 금액만 반환한다.)
,
)로 구분하며 개별 상품은 대괄호([]
)로 묶어 세미콜론(:
)으로 구분한다.IllegalArgumentException
를 발생 시키고 "[ERROR]"로 시작하는 에러 메시지 출력 후 재입력을 받는다.camp.nextstep.edu.missionutils.Console
의 readLine()
을 활용한다.[ERROR]
로 시작하는 에러 문구를 출력해야한다.camp.nextstep.edu.missionutils
에서 제공하는 Randoms
, Console
API를 활용!이번 과제를 진행하며는 다른 것 보다도 MVC를 익히고 적용시키자는 다짐을 하며 과제를 하였습니다.
MVC패턴은 기존에는 스프링 프레임워크를 학습하며 적용을 하고는 하였는데 기존에는 강의를 들으며 따라서 학습을 할 뿐이지 혼자서 MVC패턴을 구현해본 적이 없었습니다. 그래서 그런가 MVC패턴의 전반적인 개념은 알고 있어도 직접 구현을 하려니까 손이 쉽게 움직이지 않았습니다.
그래서 저는 다른 사람들의 2주차 코드리뷰, MVC테코톡 영상들, 구글링을 하며 MVC의 개념과 형태를 익히며 정리 자료를 만들었습니다. 그리고 테코톡 영상들을 찾아보며 2년 전에는 해리, 션님 이 강의를 하고 작년에는 범블비님 , 올해는 제리님 이 MVC영상을 찍을 정도로 MVC패턴은 그만큼 중요하다는 것을 느끼며 더욱 열심히 공부를 하고 이번 마지막 과제에서 가장 많은 신경을 쓴 것 같습니다.
MVC 정리자료 링크 : MVC패턴이란?
MVC 패턴은 들어가기 앞서 학습을 하며 전반적인 이론 정리를 하고, 다른 코드들을 많이 봤음에도 불구하고 코드 작성은 쉽지 않았습니다.
패턴을 적용하다보니 해당 기능은 Model로 가야하나 Controller로 가야하나..?하는 고민들을 수없이 많이 하다보니 기존에 빠르게 구현할 수 있었던 코드들도 작성하는데 엄청난 시간이 걸렸던 것 같습니다..😂😂
그렇게 과제를 진행하다보니 요구사항 정리부터 과제 완료까지 대략 12시간이라는 시간이 걸렸던 것 같습니다..다음주 토요일에 예정되어있는 최종 시험은 5시간만 주어지는데 말이죠😥😥
처음 작성한 코드의 구조는 아래의 사진과 같습니다.
가장 기본적인 구조로 Service, Repository없이 Model, View, Controller로 코드를 작성하다보니 모든 비즈니스 로직은 Model에 위치하여 Model이 하는 작업의 양이 매우 많이 있었고 코드들이 길어지다보니 코드의 가독성 또한 좋지 못한 것을 느낄 수 있었습니다.
그래서 저는 해리, 션님의 테코톡 영상 에서 션님이 잠깐동안 언급하였던 Service 레이어를 생성하며 코드의 작업을 분배고자 하였습니다. 처음에는 기존 코드의 리펙토링을 시도하였지만 코드의 결합도가 너무 높아서 리펙토링을 하기 힘들었습니다.. MVC패턴의 가장 큰 장점이 코드의 독립성과 코드의 가독성, 재사용성인데 처음 적용하였던 MVC 적용 코드는 모든 장점을 놓친 잘못된 코드라는 것을 알게 되었습니다😥 (제가 처음 작성한 MVC라 코드의 분리를 잘 못한 것 같습니다..)
그래서 저는 새로운 branch
를 다시 생성하여 처음부터 model, view, controller, service, repository로 나누어 개발을 진행했습니다.
위와 같은 방법으로 파일을 생성하며 프로그래밍을 하다보니 아래와 같은 파일 구조가 나왔습니다.
다시 작성한 코드들는 비즈니스 Model(domain)에서 분리하고 Service에 생성을 하다보니 MVC만을 사용한 코드들보다 확실히 코드의 가독성이 올라가고 재사용성이 높아진 것 같았습니다.
특히 비즈니스 코드를 분리하다보니 리펙토링을 하면서도 별 다른 문제가 발생하지 않는 것을 확인할 수 있었습니다.
싱글톤 패턴은 이번 과제를 진행하며 공부하였던 내용 중 하나입니다.
이전까지 싱글톤이라는 이름을 많이 들어봤으나 싱글톤을 언제 사용해야하는지? 싱글톤의 이점과 문제점은 무엇인지?에 대해서는 자세히 몰랐습니다.
코드 리뷰를 진행하며 다른 몇몇 지원자들이 싱글톤 패턴을 코드에 적용한 사례를 보고 해당 내용 또한 개념부터 공부를 하며 실제 코드에 적용까지 진행하였습니다.
싱글톤 패턴 정리 자료 링크 : 싱글톤(Singleton) 패턴이란?
싱글톤 패턴을 공부하다보니 이번 과제에서 제가 작성한 코드에서는 model, view, controller, service, repository로 구조를 구성하며 두번째로 프로그래밍한 과제의 Repository
부분에 적용을 하는 것이 좋다고 생각됐습니다.
저는 코드의 repository부분을 실제 데이터를 저장하고 해당 값들을 반환하는 코드들로 구성하였기 때문에 repository부분만큼은 한개의 인스턴스를 사용해야 한다고 생각했습니다.
public class ChangeRepository {
private static ChangeRepository instance = new ChangeRepository();
private static int change;
private ChangeRepository() {
}
public static ChangeRepository getInstance() {
return instance;
}
...
}
그 결과 ChangeRepository와 같은 모든 repository에 싱글톤 패턴을 적용하여 프로그램 내에서 한개의 인스턴스만이 생성되도록 하였습니다.
이번 과제에는 아래의 Coin객체를 사용하는 요구사항이 있었습니다. 평상시에 enum을 별로 사용을 하지 않았던 저는 주어진 객체 코드를 이해하기도 어려웠습니다. 그래서 enum의 기본적인 개념부터 공부를 하며 enum에 대해 익히고 무사히 사용할 수 있었습니다.
public enum Coin {
COIN_500(500),
COIN_100(100),
COIN_50(50),
COIN_10(10);
private final int amount;
Coin(final int amount) {
this.amount = amount;
}
}
enum을 사용하며 프로그래밍을 하다보니 코드의 가독성과 해당 코인 외의 새로운 코인을 추가로 생성할 수 없다는 점에서 안정성이 높아진다는 것을 느끼게 되었습니다.
Enum정리자료 : 자바의 Enum 정복하기
2주차 피드백에는 다음 사진과 같이 Java에서 제공하는 api를 적극 활용하라는 피드백이 있었습니다. java에서 많은 api들이 존재하지만 저는 그 중에서도 stream
을 공부하여 적용해보았습니다.
Coin클래스에서 파라미터로 받는 remainMoney
보다 적은 돈을 가진 코인의 값들을 list로 반환하는 getAvailableCoinValueList
의 코드를 예시로 보겠습니다. 아래의 코드는 Stream
을 적용하기 전의 코드입니다. 물론 해당 코드도 depth는 2이고 코드의 길이가 많이 길지 않아 가독성이 떨어지는거 같지는 않습니다. 하지만 저는 배우는 입장이기에 해당 코드를 stream
을 적용하여 변경시켜봤습니다.
public static List<Integer> getAvailableCoinValueList(int remainMoney) {
List<Integer> coinValueList = new ArrayList<>();
for (Coin c : Coin.values()) {
if (c.getAmount() <= remainMoney) {
continue;
}
coinValueList.add(c.getAmount());
}
return coinValueList;
}
stream을 적용한 코드는 다음과 같습니다. 코드에 stream을 적용해보니 확실히 코드의 길이가 간결해졌고 stream을 알고 있다는 가정하에서는 코드의 가독성도 높아진 것 같습니다.
public static List<Integer> getAvailableCoinValueList(int remainMoney) {
return Arrays.stream(Coin.values())
.filter(coin -> coin.getAmount() <= remainMoney)
.map(Coin::getAmount)
.sequential()
.collect(Collectors.toList());
}
stream을 학습하다보니 생각보다 많은 내용이 존재하였습니다. 내용이 너무 많은 만큼 다른 학습 내용과 다르게 따로 정리를 하지는 못하였으나 구글에서 좋은 정리 자료가 있어 공유를 합니다.
참조한 Stream정리자료
과제를 진행하며 리펙토링을 많이 하다 보니 불필요한 import들이 많이 발생하였습니다.
불필요한 import들을 하나씩 제거하다보니 1주차 피드백으로 아래와 같이 IDE의 기능을 활용하라던 피드백이 떠올라 불필요한 import제거를 해주는 단축어는 없는지 찾아봤습니다.
검색을 하다보니 실제로 불필요한 import문을 제거하는 단축키가 존재하였고 해당 기능을 적극 활용하여 불필요한 imort문들을 제거하는 작업을 진행하였습니다.
사용하지 않는 import문을 정리하는 단축키
윈도우 : Ctrl + Alt + O
Mac OS : ^ + ⌥ + O
2주차 과제를 마치고 코드 리뷰를 하던 중 jayjaehunchoi 지원자님의 제출 코드와 학습 정리 자료를 보던 중 일급 컬렉션이라는 용어를 처음 접하게 되었습니다. 난생 처음 들어보는 용어인 만큼 나중에라도 도움이 되겠지!라는 마음으로 일급 컬렉션이 무엇인지에 대해 학습을 하고 코드에 적용을 해보고자 하였습니다.
일급 컬렉션은 final
로도 컬렉션의 불변을 만들 수 없기에 하나의 컬렉션을 wrapping하여 만든 것입니다. 하지만 프로젝트를 진행하다보니 컬렉션으로 관리 할 데이터들은 다음 정보들이 존재하였습니다.
1. 상품 정보(ProductRepository)
상품 정보의 경우는 자판기가 상품을 판매하게 된다면 남은 상품의 개수를 변경시켜야 하여 일급 컬렉션을 적용은 불적합하다고 판단이 판단했습니다.
2. Machine이 보유한 각각의 Coin개수에 대한 데이터(MachineCoinRepository)
Coin의 개수는 초기 설정을 제외하고는 Coin개수가 불변하지만 초기 코인 개수의 설정 메서드에 put()
이 사용되어 일급 컬렉션을 구현하였다고는 말을 할 수 없을 것 같습니다. 또 초기 코인 개수의 설정 메서드를 put()
을 사용하지 않고 생성자 부분에서 할 수는 있었지만 그러기 위해서는 MachineCoinRepository에 적용한 싱글톤 적용을 풀어야 했기에 일급 컬렉션의 완벽한 적용은 하지 못하였습니다.
일급 컬렉션의 실제 적용은 못하였지만 새로운 지식을 쌓아갔다는 것으로 만족하며 토요일에 있을 최종 코딩테스트나 이후의 프로그래밍에서는 일급 컬렉션을 적용할 수 있으면 실제 적용을 해보려고 합니다.
일급 컬렉션 정리자료 링크 : 일급 컬렉션이란?
2주차까지 과제를 진행하였을 때 메시지를 포함한 모든 상수들은 하나의 클래스 파일을 생성하여 한 곳에서 관리를 하였습니다. 그 당시 저는 한 곳에서 모든 상수를 관리하는 것이 코드가 더 깔끔해 보인다고 생각하여 한 곳에서 관리하였습니다.
하지만 다른 지원자들의 코드 리뷰를 하며 상수를 사용되는 클래스의 상단에 정의를 하여 사용하는 코드들을 접하다보니 코드를 해당 코드를 처음 접하는 사람의 입장에서는 모든 상수를 한 개의 클래스에 모으는 방법보다 사용되는 클래스 상단에 정의를 하는게 좋을 것 같다는 생각이 들었습니다.
그래서 이번 과제에서는 에러 메시지를 제외한 모든 상수 값들은 각각의 클래스 상단에 정의를 하였으며 에러 메시지만 기존의 방법처럼 클래스를 새로 생성하여 한 곳에서 관리하였습니다.
해당 방법에는 정답은 따로 없는 것 같습니다. 하지만 앞으로도 여러 방법을 사용하여 상수들을 관리해보고 더욱 좋은 방법을 찾아보도록 노력하겠습니다.
이번 과제를 하면서 가장 아쉬웠던 점은 두번째로 작성한 코드에서는 코드를 작성하며 Test코드를 작성하지 못하였던 점이었습니다. MVC패턴을 Model, View, Controll만 적용한 첫번째 코드를 작성하였을 때는 여러 조건에 따라 Test코드를 작성하며 진행을 하였으나 Model, View, Controller, Service, Repository의 구조로 새로운 branch를 만들어 프로그래밍을 하였을 때는 시간에 쫓기는 마음이 들어 Test코드를 작성하지 못하였습니다😥
물론 첫번째로 프로그래밍하였던 Branch에서 상황별 Test코드를 작성하고 테스트를 진행하여 해당 로직들에 문제가 없는 것을 확인하였으나 새로운 구조로 작성하였던 로직들을 합치는 과정에서 예상치 못하였던 오류들이 발생하여 Test코드 부재에 대해 아쉬움이 남았습니다.
1주차 2주차 과제와 비교하였을 때 확실히 이번 과제는 난의도가 높았던 것 같습니다.
제가 MVC패턴을 적용 학습을 하며 과제를 진행하여 많은 시간이 걸린 것도 있지만 같은 과제를 다시 진행하였음에도 불구하고 많은 시간이 걸렸던 것 같습니다..
이번 주차는 MVC패턴을 학습하고 적용하는데 거의 모든 시간을 투자했다고 봐도 무방합니다.
일주일이라는 기간 동안 MVC 학습에 많은 시간을 투자하였으나 어느까지의 기능이 서비스에서 담당하여야 하고 또 어디까지의 기능이 컨트롤러서 담당해야하는지 헷갈리는 것 같습니다. 해당 내용은 우테코에 합격을 하여 꼭 배우고 싶습니다🔥🔥🔥
※ 우테코 프리코스 3주 과정에 대한 최종 후기는 새로운 글로 남기겠습니다.