Skip to content

[곽민혁] Sprint 1 #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

MinGramme
Copy link
Collaborator

@MinGramme MinGramme commented Jun 4, 2025

요구사항

기본

프로젝트 초기화

  • IntelliJ를 통해 다음의 조건으로 Java 프로젝트를 생성합니다.

  • IntelliJ에서 제공하는 프로젝트 템플릿 중 Java를 선택합니다.

  • 프로젝트의 경로는 스프린트 미션 리포지토리의 경로와 같게 설정합니다.

  • 예를 들어 스프린트 미션 리포지토리의 경로가 /some/path/1-sprint-mission 이라면:

    • Name은 1-sprint-mission
    • Location은 /some/path
    • 으로 설정합니다.
  • Create Git Repository 옵션은 체크하지 않습니다.

  • Build system은 Gradle을 사용합니다. Gradle DSL은 Groovy를 사용합니다.

  • JDK 17을 선택합니다.

  • GroupId는 com.sprint.mission로 설정합니다.

  • ArtifactId는 수정하지 않습니다.

  • .gitignore에 IntelliJ와 관련된 파일이 형상관리 되지 않도록 .idea디렉토리를 추가합니다.

도메인 모델링

  • 디스코드 서비스를 활용해보면서 각 도메인 모델에 필요한 정보를 도출하고, Java Class로 구현하세요.
  • 패키지명: com.sprint.mission.discodeit.entity
  • 도메인 모델 정의
  • 공통
  • id: 객체를 식별하기 위한 id로 UUID 타입으로 선언합니다.
  • createdAt, updatedAt: 각각 객체의 생성, 수정 시간을 유닉스 타임스탬프로 나타내기 위한 필드로 Long 타입으로 선언합니다.
  • User
  • Channel
  • Message

생성자

  • id는 생성자에서 초기화하세요.
  • createdAt는 생성자에서 초기화하세요.
  • id, createdAt, updatedAt을 제외한 필드는 생성자의 파라미터를 통해 초기화하세요.

메소드

  • 각 필드를 반환하는 Getter 함수를 정의하세요.
  • 필드를 수정하는 update 함수를 정의하세요.

서비스 설계 및 구현

  • 도메인 모델 별 CRUD(생성, 읽기, 모두 읽기, 수정, 삭제) 기능을 인터페이스로 선언하세요.
  • 인터페이스 패키지명: com.sprint.mission.discodeit.service
  • 인터페이스 네이밍 규칙: [도메인 모델 이름]Service
  • 다음의 조건을 만족하는 서비스 인터페이스의 구현체를 작성하세요.
  • 클래스 패키지명: com.sprint.mission.discodeit.service.jcf
  • 클래스 네이밍 규칙: JCF[인터페이스 이름]
  • Java Collections Framework를 활용하여 데이터를 저장할 수 있는 필드(data)를 final로 선언하고 생성자에서 초기화하세요.
  • data 필드를 활용해 생성, 조회, 수정, 삭제하는 메소드를 구현하세요.

메인 클래스 구현

  • 메인 메소드가 선언된 JavaApplication 클래스를 선언하고, 도메인 별 서비스 구현체를 테스트해보세요.
  • 등록
  • 조회(단건, 다건)
  • 수정
  • 수정된 데이터 조회
  • 삭제
  • 조회를 통해 삭제되었는지 확인
  • 기본 요구사항 커밋 태그
  • 여기까지 진행 후 반드시 커밋해주세요. 그리고 sprint1-basic 태그를 생성해주세요.

심화 요구 사항

  • 서비스 간 의존성 주입
  • 도메인 모델 간 관계를 고려해서 검증하는 로직을 추가하고, 테스트해보세요.

주요 변경 사항

  • 공통 필드를 BaseEntity 클래스 작성 후 상속
  • JCFServiceFactory 클래스 작성 (팩토리 패턴 적용)
  • Enum을 활용해 회원 상태 구현

멘토에게

  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.


//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하지 않는 또는 불필요한 클래스 파일은 과감하게 삭제해주세요.
코드를 읽는 독자에게 잘못된 정보를 전달할 수 있습니다.

import java.util.UUID;

public class BaseEntity {
private UUID id;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id 값은 절대 바뀌지 않죠? 불변성 설정을 위해서 final 키워드를 붙이는 것은 필수 입니다.

return updatedAt;
}

public void touchUpdatedAt() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

touch라는 단어를 이용하여 적절하게 의미 전달이 잘되는 메서드명 좋았습니다.


public class Channel extends BaseEntity {
private String channelName;
private List<User> users;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections 타입 변수에 저장되는 값은 변하지 않고 컬렉션 타입의 요소들만 변하기 때문에 final 키워드를 붙여주는 것이 올바른 방법입니다.

}

public void addUser(User user) {
if (!users.contains(user)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

users.contains(user) 문장이 자주 선언되는데, 이런 경우, 하나의 메서드로 빼서 구현하는 것이 코드의 가독성이나 유지보수성에 좋습니다.

import java.util.List;

public class Message extends BaseEntity {
private User user;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Message 클래스에서 의존성 주입을 받는 객체들은 절대로 변할 일이 없죠?
Setter. 함수가 없으니깐요! 그렇다면 final로 불변성을 가지고 있다는 것을 표현해주는 것이 좋고
final 키워드를 통해서 변하지 않도록 설정을 해준다면 의도하지 오류를 만날 확률이 급격하게 떨어집니다..


@Override
public User createUser(User user) {
data.add(user);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당하는 User가 추가되어도 되는지 유효성 검사 로직이 누락되었습니다.

@Override
public Optional<User> getUserByUserId(UUID userId) {
return data.stream()
.filter(u -> u.getStatus().equals(UserStatus.ACTIVE))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equals로 문자열을 비교할 때, 비교문자열.equals(비교하고 싶은 변수 or 데이터)로 쓰는 것이 좀 더 안전합니다.
ex) UserStatus.ACTIVE.equals(u.getStatus()) 이런 식으로 말이죠.
이 이유는 한번 찾아서 공부해보시기 바랍니다.

return data.stream()
.filter(ch -> ch.getId().equals(channelId))
.findFirst()
.map(channel -> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stream API를 사용할 때, map 안에서 리턴문을 작성하는 것 보다는 생략하는 문법을 사용하는 것이 좋습니다.
아래 처럼 작성하면 return문을 생략해도 updateName에서 리턴하는 String 타입의 데이터를 반환할 수 있습니다.
.map(channel -> channel.updateName(newChannelName)) // updateName이 void면 X

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants