Conversation
|
하나의 커밋으로 작업하는 것 보단 각 단계 구현마다 커밋 메세지를 남겨주시는 편이 좋습니다! |
|
.gitignore 파일이 제대로 작성 안됐습니다. |
There was a problem hiding this comment.
엔티티에 ~Entity 명시해주는 편이 좋습니다!
There was a problem hiding this comment.
엔티티 명명 규칙에 대해 말씀해 주신 부분 확인했습니다. 다만, 이번 과제 요구사항(명세서)에 정의된 엔티티명을 준수하기 위해 현재와 같이 작성하였습니다. 다음 프로젝트나 실무에서는 제안해 주신 관례를 적극 고려해 보겠습니다.
| import java.util.UUID; | ||
|
|
||
| @Repository | ||
| @ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") |
| @Override | ||
| public List<BinaryContentResponse> findAllByIdIn(List<UUID> ids) { | ||
| return ids.stream() | ||
| .map(binaryContentRepository::findById) |
There was a problem hiding this comment.
조회 할 때 부수효과 발생 되므로 제외해줘야 합니다!
There was a problem hiding this comment.
그리고 전체 목록을 한번에 조회해주는 편이 성능 관점에서도 더 낫습니다.
There was a problem hiding this comment.
기존에 반복문 안에서 단건 조회를 하던 로직을, Repository에서 findAllByIdIn으로 한 번에 조회한 뒤 DTO로 변환하도록 수정했습니다.
@Override
public List<BinaryContentResponse> findAllByIdIn(List<UUID> ids) {
return binaryContentRepository.findAllByIdIn(ids).stream()
.map(this::toResponse)
.collect(Collectors.toList());
}최종적으로 부수효과 문제 해결 및 전체 목록 한번에 조회하도록 수정하였습니다!
| return readStatusRepository.findAll().stream() | ||
| .anyMatch(rs -> rs.getChannelId().equals(channel.getId()) | ||
| && rs.getUserId().equals(userId)); |
There was a problem hiding this comment.
@Override
public List<ChannelDto> findAllByUserId(UUID userId) {
// 모든 채널 조회 -> PUBLIC 채널은 모두 포함 / PRIVATE 채널은 해당 유저가 ReadStatus를 가지고 있는 경우만 포함
// 부수효과 제거를 위해 스트림 외부에서 유저의 접근 가능한 channelId를 한번에 조회
Set<UUID> accessiblePrivateChannelIds = readStatusRepository.findAllByUserId(userId).stream()
.map(ReadStatus::getChannelId)
.collect(Collectors.toSet());
return channelRepository.findAll().stream()
.filter(channel -> {
if (channel.getType() == ChannelType.PUBLIC) {
return true;
} else {
// PRIVATE 채널인 경우, 유저가 해당 채널에 대한 ReadState를 가지고 있는지 확인
return accessiblePrivateChannelIds.contains(channel.getId());
}
})
.map(this::toChannelDto)
.collect(Collectors.toList());
}- ReadStatusRepository에 findAllByUserId 메서드를 추가하여 외부 I/O를 1회로 줄였습니다.
- 스트림 실행 전, 해당 유저가 접근 가능한 channelId 목록을 한 번에 조회하여 Set 컬렉션으로 할당했습니다.
- 스트림 내부의 filter에서는 미리 메모리에 올려둔 Set의 contains 메서드를 활용하여 검증하도록 변경했습니다.
| UserStatus userStatus = userStatusRepository.findByUserId(user.getId()) | ||
| .orElseThrow(() -> new NoSuchElementException("UserStatus를 찾을 수 없습니다.")); |
There was a problem hiding this comment.
public List<UserDto> findAllUsers() {
List<User> allUsers = userRepository.findAll();
List<UserStatus> allUserStatuses = userStatusRepository.findAll();
Map<UUID, UserStatus> statusMap = allUserStatuses.stream()
.collect(Collectors.toMap(
UserStatus::getUserId,
Function.identity()
));
return allUsers.stream()
.map(user -> {
UserStatus userStatus = statusMap.get(user.getId());
if (userStatus == null) {
throw new NoSuchElementException("UserStatus를 찾을 수 없습니다. userId: " + user.getId());
}
return new UserDto(
user.getId(),
user.getCreatedAt(),
user.getUpdatedAt(),
user.getUsername(),
user.getEmail(),
user.getProfileId(),
userStatus.isOnline()
);
}).collect(Collectors.toList());
}- User와 UserStatus를 각각 한 번씩만 조회한 후, Map을 활용해 매핑하도록 변경하였습니다.
.gitignore 수정 완료했습니다. |
해당 미션 시작 시 Fork 저장소의 Upstream 설정이 누락되었습니다. 이를 해결하는 과정에서 미숙하게 Rebase 대신 Squash를 사용하여 커밋 내역이 하나로 통합되었습니다. 현재는 Upstream 설정 및 올바른 Rebase 프로세스를 숙지하였으며, 이후 미션부터는 단계별 구현 사항이 커밋 히스토리에 잘 드러나도록 관리하겠습니다. |
3️⃣ 스프린트 미션 3
스프린트 미션 2
[SB] 스프린트 미션 2
📝 요구사항 - 1차
1️⃣ 기본 요구사항
📍프로젝트 초기화
IntelliJ에서 제공하는 프로젝트 템플릿 중 Java를 선택합니다.
프로젝트의 경로는 스프린트 미션 리포지토리의 경로와 같게 설정합니다.
예를 들어 스프린트 미션 리포지토리의 경로가
/some/path/1-sprint-mission이라면:1-sprint-mission/some/pathCreate Git Repository옵션은 체크하지 않습니다.Build system은 Gradle을 사용합니다.
Gradle DSL은 Groovy를 사용합니다.
JDK 17을 선택합니다.
GroupId는
com.sprint.mission으로 설정합니다.ArtifactId는 수정하지 않습니다.
.gitignore에 IntelliJ와 관련된 파일이 형상관리 되지 않도록.idea디렉토리를 추가합니다.📍도메인 모델링
📍서비스 설계 및 구현
📍메인 클래스 구현
2️⃣ 심화 요구 사항
📍서비스 간 의존성 주입
힌트: Message를 생성할 때 연관된 도메인 모델 데이터 확인하기
📝 요구사항 - 2차
1️⃣ 기본 요구사항
📍File IO를 통한 데이터 영속화
객체 직렬화/역직렬화 가이드
📍서비스 구현체 분석
📍레포지토리 설계 및 구현
2️⃣ 심화 요구 사항
📍관심사 분리를 통한 레이어 간 의존성 주입
🔄 주요 변경사항
📸 스크린샷
🙇🏽♂️ 멘토에게
🏔️ 프로젝트 마일스톤
📝 요구사항
✏️ 기본 요구사항
📌 Spring 프로젝트 초기화
3.4.0입니다.com.sprint.mission입니다.discodeit입니다.Jar입니다.application.properties파일을yaml형식으로 변경하세요.DiscodeitApplication의 main 메서드를 실행하고 로그를 확인해보세요.📌 Bean 선언 및 테스트
JavaApplication에서 테스트 했던 코드를DiscodeitApplication에서 테스트해보세요.JavaApplication의 main 메서드를 제외한 모든 메서드를DiscodeitApplication클래스로 복사하세요.JavaApplication의 main 메서드에서 Service를 초기화하는 코드를 Spring Context를 활용하여 대체하세요.JavaApplication의 main 메서드의 셋업, 테스트 부분의 코드를DiscodeitApplication클래스로 복사하세요📌 Spring 핵심 개념 이해하기
JavaApplication과DiscodeitApplication에서 Service를 초기화하는 방식의 차이에 대해 다음의 키워드를 중심으로 정리해보세요.📌 Lombok 적용
📌 비즈니스 로직 고도화
🔥 추가 기능 요구 사항
시간 타입 변경하기
Instant로 통일합니다.새로운 도메인 추가하기
id,createdAt,updatedAt)를 포함합니다.ReadStatusUserStatusBinaryContentupdateAt필드는 정의하지 않습니다.User,Message도메인 모델과의 의존 관계 방향성을 잘 고려하여id참조 필드를 추가하세요.DTO 활용하기
DTO란?
DTO(Data Transter Object)란 데이터를 전달하기 위한 단순한 객체를 말합니다.
이어지는 요구사항을 해결하려다보면, 도메인 모델의 일부 정보만 포함하거나, 여러 도메인 모델의 정보를 합친 데이터 모델이 필요한 경우가 생길거에요.
이런 경우에 도메인 모델을 특정 상황만을 위해 수정하기 보다는 DTO를 정의해서 해결하는 것이 바람직합니다.
뿐만 아니라 메서드 파라미터가 많아지거나, 그룹핑하고 싶을 때에도 유용할 수 있어요.
DTO를 정의할 때는 class 대신 Record를 활용하면 더 편리합니다.
UserService 고도화
createusername과email은 다른 유저와 같으면 안됩니다.UserStatus를 같이 생성합니다.find,findAllupdatedeleteBinaryContent(프로필),UserStatusAuthService 구현
loginusername,password과 일치하는 유저가 있는지 확인합니다.ChannelService 고도화
createUser의 정보를 받아User별ReadStatus정보를 생성합니다.name과description속성은 생략합니다.findUser의id정보를 포함합니다.findAllUser의id정보를 포합합니다.User가 볼 수 있는 Channel 목록을 조회하도록 조회 조건을 추가하고, 메서드 명을 변경합니다.findAllByUserIdupdatedeleteMessage,ReadStatusMessageService 고도화
고도화
createfindAllChannel의 Message 목록을 조회하도록 조회 조건을 추가하고, 메서드 명을 변경합니다.findallByChannelIdupdatedelete의존성
ReadStatusService 구현
createChannel이나User가 존재하지 않으면 예외를 발생시킵니다.Channel과User와 관련된 객체가 이미 존재하면 예외를 발생시킵니다.findid로 조회합니다.findAllByUserIduserId를 조건으로 조회합니다.updatedeleteid로 삭제합니다.의존성
UserStatusService 고도화
createUser가 존재하지 않으면 예외를 발생시킵니다.User와 관련된 객체가 이미 존재하면 예외를 발생시킵니다.findid로 조회합니다.findAllupdateid파라미터, 수정할 값 파라미터updateByUserIduserId로 특정User의 객체를 업데이트합니다.deleteid로 삭제합니다.의존성
BinaryContentService 구현
createfindid로 조회합니다.findAllByIdInid목록으로 조회합니다.deleteid로 삭제합니다.의존성
새로운 도메인 Repository 구현체 구현
✏️ 심화 요구사항
Bean 다루기
discodeit.repository.type설정값에 따라 Repository 구현체가 정해집니다.jcf이거나 없으면 JCF*Repository 구현체가 Bean으로 등록되어야 합니다.file이면 File*Repository 구현체가 Bean으로 등록되어야 합니다.🔄 주요 변경사항
📸 스크린샷
🙇🏽♂️ 멘토에게