Skip to content

Conversation

@HuInDoL
Copy link
Collaborator

@HuInDoL HuInDoL commented Apr 20, 2025

요구사항

기본

File IO를 통한 데이터 영속화

  • 다음의 조건을 만족하는 서비스 인터페이스의 구현체를 작성하세요.

    • 클래스 패키지명: com.sprint.mission.discodeit.service.file
    • 클래스 네이밍 규칙: File[인터페이스 이름]
    • JCF 대신 FileIO와 객체 직렬화를 활용해 메소드를 구현하세요.
  • Application에서 서비스 구현체를 File*Service로 바꾸어 테스트해보세요.

서비스 구현체 분석

  • JCF*Service 구현체와 File*Service 구현체를 비교하여 공통점과 차이점을 발견해보세요.
    • "비즈니스 로직"과 관련된 코드를 식별해보세요.
    • "저장 로직"과 관련된 코드를 식별해보세요.

레포지토리 설계 및 구현

  • "저장 로직"과 관련된 기능을 도메인 모델 별 인터페이스로 선언하세요.
    • 인터페이스 패키지명: com.sprint.mission.discodeit.repository
    • 인터페이스 네이밍 규칙: [도메인 모델 이름]Repository
      [x] 다음의 조건을 만족하는 레포지토리 인터페이스의 구현체를 작성하세요.
    • 클래스 패키지명: com.sprint.mission.discodeit.repository.jcf
    • 클래스 네이밍 규칙: JCF[인터페이스 이름]
    • 기존에 구현한 JCF*Service 구현체의 "저장 로직"과 관련된 코드를 참고하여 구현하세요.
  • 다음의 조건을 만족하는 레포지토리 인터페이스의 구현체를 작성하세요.
    • 클래스 패키지명: com.sprint.mission.discodeit.repository.file
    • 클래스 네이밍 규칙: File[인터페이스 이름]
    • 기존에 구현한 File*Service 구현체의 "저장 로직"과 관련된 코드를 참고하여 구현하세요.

심화

관심사 분리를 통한 레이어 간 의존성 주입

  • 다음의 조건을 만족하는 서비스 인터페이스의 구현체를 작성하세요.
    • 클래스 패키지명: com.sprint.mission.discodeit.service.basic
    • 클래스 네이밍 규칙: Basic[인터페이스 이름]
    • 기존에 구현한 서비스 구현체의 "비즈니스 로직"과 관련된 코드를 참고하여 구현하세요.
  • 필요한 Repository 인터페이스를 필드로 선언하고 생성자를 통해 초기화하세요.
  • "저장 로직"은 Repository 인터페이스 필드를 활용하세요. (직접 구현하지 마세요.)
  • [ ] Basic*Service 구현체를 활용하여 테스트해보세요.
  • JCF*Repository 구현체를 활용하여 테스트해보세요.
  • File*Repository 구현체를 활용하여 테스트해보세요.
  • 이전에 작성했던 코드(JCF*Service 또는 File*Service)와 비교해 어떤 차이가 있는지 정리해보세요.

주요 변경사항

스크린샷

image

멘토에게

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

  • File*Repository에서 단일 객체를 받아도 list로 저장하고 싶은데 어떻게 구현해야 할지 알려주시면 감사하겠습니다.

  • 첫번째 사진은 제가 미완성인 상태로 제출 직전에 정리한 내용인데, 스프린트 미션 2 요구사항을 이렇게 설계해 나가야 하는건지 모르겠습니다. 먼저 Test메소드를 대략적으로 JavaApplication2에 만든 후에 이를 바탕으로 Test 메소드를 구현하기 위해 미션 요구사항의 전체 인터페이스와 클래스 간의 관계를 설정해나가야 하는게 좋을까요? 미션 요구사항을 읽긴 했는데, 요구사항을 순서대로 만들다 보니 엉키는 코드가 생기고, 종국엔 BasicUserService의 내용을 하나도 만들지 못했습니다. 어떤 순서로 클래스를 작성해나가는게 좋을까요? 설계가 제대로 안되니 진전이 없어서 너무 답답합니다...

  • https://www.notion.so/2-1da8d8ad920780bfbeafe6aec610fcf0?pvs=4 이 주소는 제 스프린트 2 설계가 적힌 노션인데, 너무 허술하고 부족한 점이 많습니다. 여기서 고쳐야 할 점과 부족한 점을 살펴봐주시면 감사하겠습니다. 그리고 노션 공유가 처음이고 방법을 몰라서 아마 이 링크로 접근이 안될 것 같은데 제가 멘토님을 제 노션에 초대할 수 있게 도와주셨으면 합니다. 감사합니다.

    • 스프린트 미션 요구사항을 다시 읽어보고 설계 내용을 구현하는 연습을 하려고 하는데, PR한 파일을 바탕으로 재구성하는 쪽으로 해보는게 좋을까요, 아니면 스프린트 미션 1 코드를 바탕으로 다시 해보는게 좋을까요?
    • 보시면 아시겠지만, crud 메소드 용어가 중구난방입니다. *Service 인터페이스와 Repository 인터페이스의 메소드도 다릅니다. 특히 FileRepository에는 오버라이딩 하지 않은 save, load 메소드와 오버라이딩 한 create, find 등이 각각 존재합니다. 코드를 짜다가 많이 막히고 난 후 제 생각에 제가 작성한 방식은 많이 별로인 것 같습니다. 어떻게 메소드를 나누는게 좋았을까요?

@HuInDoL HuInDoL requested a review from codingjigi April 20, 2025 14:55
@HuInDoL HuInDoL added 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 미완성🛠️ 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다. labels Apr 20, 2025
Copy link
Collaborator

@codingjigi codingjigi left a comment

Choose a reason for hiding this comment

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

요구사항이 복잡하다보면 어디서부터 손을 대야 될지 모르는 상황이 오죠.. 저도 공감하는 부분입니다. 저 같은 경우는 무엇을 만들 것인지 먼저 공책에 정리하는 편이에요 (요구사항)

흐름 순으로 컨트롤러 -> 서비스 -> 레포지토리 계층으로 차근차근 작성해보세요.

단일 객체라도 리스트로 저장하고 싶다면

public interface FileRepository {
    void save(File file); // 단일 저장
    void saveAll(List<File> files); // 다중 저장
}
public class MemoryFileRepository implements FileRepository {
    private final List<File> storage = new ArrayList<>();

    @Override
    public void save(File file) {
        saveAll(List.of(file)); // 내부적으로 saveAll 호출
    }

    @Override
    public void saveAll(List<File> files) {
        storage.addAll(files);
    }
}

위와 같은 방법으로 할 수 있을 것 같아요.

네이밍 같은 경우 정답은 없습니다. 프로젝트에서 통일하는게 원칙이긴해요 ㅎㅎ
저는 레포지토리에서는 save, find 서비스에서는 create, get 애용합니다.

노션은 권한이 없어서 안들어가지는 것 같아요. 확인 부탁드립니다!

화이팅입니다 정현님!

super.updateUpdatedAt();
public void updateChannel(String newChannelName, String newDescription) {
boolean anyValueUpdated = false;
if (newChannelName != null && !newChannelName.equals(this.channelName)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

유효성 검사 잘하셨네요 👍


public class User extends Base {
public class User extends Base implements Serializable {
private transient String RRN;
Copy link
Collaborator

Choose a reason for hiding this comment

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

이건 뭘까요?

Copy link
Collaborator Author

@HuInDoL HuInDoL Apr 24, 2025

Choose a reason for hiding this comment

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

아 RRN은 주민등록번호입니다! transient 한번 써보고 싶어서 그냥 임의로 만들어봤어요

Copy link
Collaborator

Choose a reason for hiding this comment

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

알아보기 쉽게 주석을 달면 좋을 것 같아요~


public interface UserRepository { // 저장소에 저장해주는 것

void create(User user); // User user 말고 Object o로도 가능한가?
Copy link
Collaborator

Choose a reason for hiding this comment

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

오브젝트로 가능하지만 오브젝트는 모든 객체 상위 타입이기 때문에 비추천합니다~

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

옹 감사합니다!

objectList.add(object);
}
} catch (FileNotFoundException e) {
System.out.println("메시지 파일을 찾을 수 없습니다."); // 비즈니스 로직
Copy link
Collaborator

Choose a reason for hiding this comment

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

예외 메시지로 넣어보는것도 좋아보이네요

public List<User> load() {
List<User> objectList = new ArrayList();

Path path = Paths.get("/data/users.txt");
Copy link
Collaborator

Choose a reason for hiding this comment

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

path 는 상수 처리해도 될 것 같아요

@codingjigi codingjigi merged commit cd929c2 into codeit-bootcamp-spring:임정현 Apr 29, 2025
.orElseThrow(() -> new NoSuchElementException(id + "ID를 가진 채널을 찾을 수 없습니다."));
channel.updateChannel(newName, newDescription);

saveChannel(this.data.values().stream().toList());
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 코드는 불필요한 변환으로 보여요.

new ArrayList<>(this.data.values())

이렇게 선언하면 깔끔하고 명확해질 것 같네요.

ObjectInputStream channelOIS = new ObjectInputStream(new FileInputStream(FILE_PATH));
) {
for (Channel channel : (List<Channel>) channelOIS.readObject()) {
channels.add(channel);
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기서 add() 하는 이유가 뭘까요?

private Map<UUID, Channel> data;

public FileChannelService() {
this.data = new HashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

this.data는 제거하고 모든 CRUD 동작 시 loadChannels() 후 조작하는 방식으로 일관성 유지하는게 좋아보입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 미완성🛠️ 스프린트 미션 제출일이지만 미완성했습니다. 죄송합니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants