long(primitive)과 Long(wrapper)의 차이를 구분하게 되었고, 각각 어떤 경우에 사용해야 하는지 알게 되었다. (primitive - 기본형, wrapper - 컬렉션 사용, null 허용 등)
autoboxing/unboxing에 따른 불필요한 형변환도 코드를 작성하는 데에 고려하면 좋다는 것을 알 수 있었다.
단, 해당 필드 타입은 요구사항에 타입이 명시되어 있었기에 수정하지 않았다.
메서드명 setUpdatedAt() → updateTimestamp()처럼 setter 메서드로 오해할 수 있는 네이밍을 수정했고, 클래스명은 channelMenu -> ChannelMenu처럼 카멜케이스로 통일했다.
오해의 여지가 있는 네이밍이 협업에 큰 영향을 미칠 수 있다는 것을 알게 되었다.
JCFChannelService를 직접 사용하는 것보다는 ChannelService 인터페이스를 통해 객체를 다루도록 수정했다. 이로써 다형성이 적용되고, 코드의 유연성/확장성에 긍정적인 영향을 주었다.
채널 리스트를 List 타입으로 구현하였는데, 이는 데이터 양이 엄청나게 많아질 경우 비효율적일 수 있어 Map<UUID, Channel> 타입으로 수정하였다.
데이터 타입은 시간복잡도를 고려하여 선택해야 하는 것을 알게 되었다.
Scanner는 AutoCloseable 구현체이므로, try-with-resources문을 활용하는 것이 자원 관리를 깔끔하게 할 수 있다. 이는 close()가 호출되지 않는 상황에 생기는 리소스 누수를 방지할 수 있다.
try-catch를 통한 예외 처리시, catch 부분에 무언가 로직을 추가하고 싶어지는 유혹을 조심하고, 예외를 핸들링하는 목적에만 집중하기serialVersionUID은 각 클래스별 다른 값으로 설정해주어야 한다.
각 도메인은 자신의 역할과 데이터에만 집중하도록 설계하는 것이 좋다.
설계시, '더 근본적인 도메인을 기준으로 설계한다'를 고려하여 의존성을 따져보는 게 좋다.
Channel이 Message, User리스트를 직접 가지고 있는 구조는 의존성이 복잡해지고 도메인 간 결합도가 높아진다.
private List<Message> messageList; // 채널에 등록된 메시지 리스트
private List<User> joinUserList; // 채널에 참여중인 유저리스트
예를 들어 Message는 Channel이 있어야만 존재할 수 있으므로, Message가 ChannelId를 가지고 있는게 더 자연스럽다.

레포지토리의 메서드 명 중 read() -> get() 또는 find() 로 바꾸는게 의미를 더 명확히 전달할 수 있다.
또한 ~From은 인스턴스를 생성할때 주로 사용되므로, 조회 조건을 나타내는 경우에는 ~By를 사용하는 것이 더 적절하다.
Channel이 User를 의존하는 방향이라면, 코드를 다음과 같이 수정하는 것이 좋다.
// AS IS
if (targetUser.getJoinChannelList().contains(ch)) {
// TO BE
if (channelService.findChannelsByUserId(UUID id).contains(ch)) {
도메인의 의존 방향을 잘못 설계하면 이후 코드 전체에 영향을 미쳐 큰 리펙토링이 필요해질 수 있다.
따라서 도메인 설계시 의존성 방향을 충분히 고민해야 한다.
leaveChannel(User user, Channel channel) → leaveChannel(UUID userId, UUID channelId) 로 수정하였다.
서비스 메서드의 파라미터로는 객체 전체가 아닌, 식별자만 넘기는 것이 더 적절하다는 점을 배웠다.
ChatService 등과 같은 서비스로 분리)이번 미션을 통해, 초반 도메인 설계의 중요성을 깨달았다.
특히, 각 도메인간의 의존성을 생각하며 설계하는 것이 중요하고 설계가 잘못된 경우 수정하는 데에 많은 시간이 소요되므로, 초반 설계에 충분한 시간을 들이는 것이 이후 개발 효율성에 중요하다는 것을 알게 되었다.
Scanner를 통해 사용자의 입력을 받아 수행하게 구현하였는데, 테스트가 오히려 복잡해졌고, 매번 데이터를 새로 입력해야 하는게 번거로웠다.