간단한 디스코드 프로그램 만들기_ 피드백3(매개변수 변경 ,메서드 이관)

2026. 1. 15. 16:11·코드잇 스프린트/실습

Feedback1)ChannelService의 create() 받는 부분을 User owner에서 UUID ownerId로 변경하여라.

이유.

  • id로 받는 이유는 객체로 받을경우 그 객체가 내가 찾는 객체인지 보장할 수 없다.
  • 주소값이 같은걸로 판단할것인지, User의 name만 같으면 같은 객체인지 판단할 수 없다.
  • 또 FE에서는 js를 쓰기때문에 객체로 넘길 수 없다. 그래서 식별자를 통해 전달하고 해당 id를 가지고 객체를 직접조회해야 함.

 

JCFChannenlService(구현체)

@Override
    public Channel create(String channelName, String type, User owner){
        Channel channel = new Channel(channelName,type,owner);
        data.put(channel.getId(),channel); //data에 key value 값으로 넣음.
        return channel;
    }

create()의 매개변수로 User owner를 UUID ownerId로 받게하라는 피드백이 있다.

User owner을 UUID ownerId로 변경하면 생성자에 영향이 있다.

create()문은 말그대로 채널 객체를 생성하는것.

 

Channel 클래스

public class Channel extends Common{

    private String channelName;
    private String type;
    private User owner;
    private List<User> userList;
    private List<Message> messageList;


    public Channel(String channelName, String type, User owner){
        //super();
        this.channelName = channelName;
        this.type = type;
        this.owner = owner; //채널의 소유주
        this.userList = new ArrayList<>();
        this.userList.add(owner); // 소유자는 기본 참여자;
    }

Channel의 User owner 자체를 UUID ownerId로 바꾸고 매개변수도 UUID ownerId로 한다.

그럼 생성자내 User 객체가 없고,

생성도 못하므로 채널이 생성될때 해당 채널에 소유주를 추가하는 userList.add는 create문 쪽으로 빠져야될거같다.

 

  • User owner 필드를 UUID ownerId로 변경
  • 생성자내 userList.add를 JCFChannelService의 create()문 내로 따로 빼기

[수정후] JCFChannelService

@Override
    public Channel create(String channelName, String type, UUID ownerId){
        Channel channel = new Channel(channelName,type,ownerId);
        User user = userService.findById(ownerId);
        channel.setUserList(user);//방장을 Channel의 List<User>에 add
        data.put(channel.getId(),channel); //data에 key value 값으로 넣음.
        return channel;
    }
  • UUID ownerId로 받게 수정완료
  • Channel내 List<User> userList에 채널 생성할때 방장(user)를 넣기로함.
  • userSevice.findById(ownerId)를 통해 user객체를 만들고 Setter를 통해 필드에 접근해서 add를 하도록 수정.

[재수정]

  • Channel의 User owner필드는 그대로 둔다. -> 생성자도 User owner로 그대로 둔다.
  • JCFChannelService 측에서 객체 만들고 생성자에는 객체 전달하는식으로 코드 수정.

[재수정한 JCFChannelService]

@Override
    public Channel create(String channelName, String type, UUID ownerId){
        User owner = userService.findById(ownerId);
        Channel channel = new Channel(channelName,type,owner);
        channel.getUserList.add(owner) //방장 채널에 넣기
        data.put(channel.getId(),channel); //data에 key value 값으로 넣음.
        return channel;
    }
  • create()는 id로 받게 수정.
  • id를 통해 User 객체 만들어서 생성자로 전달.
  • userList.add(owner)는 Service 단으로 뺐다.

Feedback2)반환 타입을 User로 하면 유저쪽으로 이관해야되는지 고민을 해야된다.

 

JCFChannelService

@Override
    public List<User> enter(UUID channelId,UUID userId){ //채널 id가 들어온다.
        Channel channel = findById(channelId);
        User user = userService.findById(userId);
        channel.enter(user);
        user.addChannel(channel);

        return channel.getUserList();

    }

    @Override
    public List<User> leave(UUID channelId,UUID userId){
        Channel channel = findById(channelId);
        User user = userService.findById(userId);

        channel.leave(user);
        user.removeChannel(channel);

        return channel.getUserList();
    }
  • 현재 enter와 leave는 채널 입장,퇴장 메서드이다.
  • 입장을 하고, 참석돼있는 userList를 보여준다.
  • 단순히 입장및 퇴장의 기능만 하도록 하고 조회는 다른 메서드를 통해 하도록한다.
  • List<User>로 돼있는 타입을 void로만 수정

Feedback3)

참여하고있는 채널에 입장하면 예외/참여안하고 있는 채널에서 나가려하면 예외 코드를 도메인객체가 아닌 Service로 이관

이유.

  • 도메인 클래스는 필드와 필드의 getter,setter만 유지하는게 좋다.
  • 정책이 변경되면(입퇴실에 대한) 도메인 객체까지 수정사항이 전파될 수 있다.

 

Channel(도메인 객체)

public void enter(User user){
        if(userList.contains(user)){
            throw new IllegalArgumentException("이미 참여하고 있는 사용자입니다.");
        }
        userList.add(user);
    }

    public void leave(User user){
        if(user.equals(owner)){
            throw new IllegalArgumentException("방장은 퇴장할 수 없습니다.");
        }
        if(!(userList.contains(user))){
            throw new IllegalArgumentException(user.getUserName() + "가 채널에 없습니다.");
        }
        userList.remove(user);
    }

이걸 Service 단으로 옮기자.

 

수정후 JCFChannelService

@Override
public void enter(UUID channelId,UUID userId){ //채널 id가 들어온다.
    Channel channel = findById(channelId);
    User user = userService.findById(userId);
    if(channel.getUserList().contains(user)){
        throw new IllegalArgumentException("이미 참여하고 있는 유저입니다.");
    }
    channel.getUserList().add(user);
    user.addChannel(channel);

}

@Override
public void leave(UUID channelId,UUID userId){
    Channel channel = findById(channelId);
    User user = userService.findById(userId);

    if(user.equals(channel.getOwner())){
        throw new IllegalArgumentException("방장은 퇴장할 수 없습니다.");
    }
    channel.getUserList().remove(user);
    user.removeChannel(channel);
}

Channel의 enter,leave를 Service로 이관하였다.

빠른 정책변경이 가능하다.


Feedback4)

채널을 찾는 기능은 채널 서비스로, 메시지를 찾는 기능은 메시지 서비스로 이관하자.

이유: UserService에 둘경우 확장성이 낮음./ 각각에 두면 도메인 책임이 강해짐. 확장성 좋음.

 

 

JCFUserService

public List<Channel> selectChannel(UUID id){
        User user = findById(id);
        return user.getChannelList();
    }

    public List<Message> selectMessage(UUID id){
        User user = findById(id);
        return user.getMessageList();
    }

User 기준에서 User의 채널과 메시지를 select 하는거라 UserService에 넣었는데

각각 ChannelService와 MessageService로 나눠야함.

 

JCFMessageService

public List<Message> findByUser(UUID userId){
        User user = userService.findById(userId);
        return user.getMessageList();
    }

UserService에 있던걸 MessageService로 옮겼다.

userId를 받고 객체 만들어서 객체 접근해서 messageList 가져오게 하였다.

 

 

JCFChannelService

public List<Channel> findByUser(UUID userId){ //특정유저가 참여하고 있는 채널
    User user = userService.findById(userId);
    return user.getChannelList();
}

ChannelService도 마찬가지.


 

Feedback5)

update()메서드를 유저이름만 수정할수도, 이메일만수정할수도등 원하는 데이터만 수정할 수 있게 기능을 유연하게 만들어봐라.

 

[현재 JCFUserServie의 코드]

@Override
    public User update(UUID id,String userName,String email,String status){
        User user = findById(id);
        user.setUserName(userName);
        user.setEmail(email);
        user.setStatus(status);
        return user;
    }
  • update를 하려면 수정하고싶지않은 다른 매개변수도 보내줘야한다.
  • Optional.ofNullable()을 사용해서 바꾸고싶은 값만 바뀌도록 수정해보자.

 Optionanl이란?

  • Optional<T>는: T타입 값이 있을수도, 없을수도 있다를 표현하는 객체
  • null 직접 사용을 줄인다.
  • NPE방지
  • 없을 수 있음을 코드로 드러낸다.
Optional.ofNullable(value)

null이면 비어있는 Optionanl, 값이 있으면 그 값을 감싼 Optional

 

예제)Map에서 값 조회

User user = data.get(id);

id가 없으면 null

 

Optional<User> userOpt = Optional.ofNullable(data.get(id));

이걸 Optional로 감싸면 null일때 아닐때를 NPE 방지할 수 있다.

data.get(id)값이 null이면 userOpt = Optional.empty()라는 뜻이다.

 

User user = Optional.ofNullable(data.get(id))
                    .orElseThrow(() -> 
                        new IllegalArgumentException("유저 없음")
                    );

이런식으로 null일때 예방가능.

 

update를 어떻게 바꿔야되지?

  •  id는 꼭 필요하고, userName이나 email,status가 null이라면 getName하고 null이 아니라면 setName(name)

내가 생각한 코드

@Override
    public User update(UUID userId,String userName,String email,String status){
        User user = findById(userId);
        Optional.ofNullable(user).map(User :: setUserName(userName)).orElse(User ::getUserName());

userId로 user객체 만들고,

Optional.ofNullable로 user 넣어서 값이 있다면 setUserName을 하고,

값이 없다면 getUserName을 하는건데

 

user객체를 만들었을때 해당 객체가 userId에 따라 있거나 없는걸로 이미 결정이 남.

따라서 위 코드같이 하면 안됨.

 

[수정후 코드]

@Override
    public User update(UUID userId,String userName,String email,String status){
        if (userId == null) {//Optional은 값이 없을수도 있다의 의미라서 userId는 Optional을 쓰지않는다.
            throw new IllegalArgumentException("수정할 유저ID를 입력해주세요");
        }
        User user = findById(userId);
        Optional.ofNullable(userName).ifPresent(user :: setUserName);//userName값이 있으면 setter
        Optional.ofNullable(email).ifPresent(user :: setEmail);
        Optional.ofNullable(status).ifPresent(user :: setStatus);

        return user;

    }
  • 이렇게 하면 똑같이 main에서 update를 하려면 모든 매개변수를 입력해줘야한다.
  • 수정 안할 값에도 null을 억지로 채워야한다.
userService.update(userId, null, "test@gmail.com", null);

이런식으로 ...

 

이것이 나중에 DTO로 받을때 쉽게 대응할 수 있다.

'코드잇 스프린트 > 실습' 카테고리의 다른 글

[sprint5] 디스코드 어플리케이션 채널 생성시 NPE 발생  (0) 2026.02.23
BasicUserService내 update 기능 피드백.  (0) 2026.01.29
ReadStatus,UserStatus,BinaryContent 도메인 추가(왜 추가하는거고, 어떤 역할을 하는지?)  (0) 2026.01.28
피드백) 단건조회는 null 반환 가능성이 있어 Optional로 래핑해라  (0) 2026.01.21
간단한 디스코드 프로그램 만들기_ 각 필드에 List 추가해서 기능 추가하기.  (0) 2026.01.14
'코드잇 스프린트/실습' 카테고리의 다른 글
  • BasicUserService내 update 기능 피드백.
  • ReadStatus,UserStatus,BinaryContent 도메인 추가(왜 추가하는거고, 어떤 역할을 하는지?)
  • 피드백) 단건조회는 null 반환 가능성이 있어 Optional로 래핑해라
  • 간단한 디스코드 프로그램 만들기_ 각 필드에 List 추가해서 기능 추가하기.
과컴
과컴
벡엔드 개발자 최소기준 맞추겠습니다.
  • 과컴
    곽의 프로그램
    과컴
  • 전체
    오늘
    어제
    • 분류 전체보기 (76)
      • 위클리페이퍼 (6)
      • 파이썬 (4)
      • 코드잇 스프린트 (48)
        • Spring 이론 (7)
        • Java이론 (11)
        • 실습 (23)
      • 백엔드 개발자 최소기준 (1)
      • 코딩테스트 (5)
        • 알고리즘 (0)
        • SQL (1)
      • Git (5)
      • 스프링부트 핵심가이드 (1)
      • 트러블 슈팅 (2)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    파이썬
    혼공파
    백준1075번
    백준브론즈
    백준1152
    파이썬입문
    백준2576
    문자열
    파이썬기초
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
과컴
간단한 디스코드 프로그램 만들기_ 피드백3(매개변수 변경 ,메서드 이관)
상단으로

티스토리툴바