1. 프로젝트 진행 상황 및 계획
🥇 Spring 심화 프로젝트 필수 과제 2단계 끝내기 (진행 전, 25.01.01 완료 목표)
🥈 심화 Spring 3주 차 완강하기 (진행 중, 25.01.01 완료 목표)
🥉 Spring 심화 프로젝트 도전 과제 일정 계획하기 (진행 전, 25.01.02 완료 목표)
4️⃣ Spring 심화 프로젝트 필수 과제 1단계 끝내기 (완료)
5️⃣ 심화 Spring 2주 차 완강하기 (완료)
2. Spring 심화 프로젝트 필수 과제 1단계: 일정 관리 앱 Develop 보완하기
(1) [링크] 보완 전 코드
(2) [링크] 보완 후 코드
(3) 보완한 부분
① 세터(Setter) 대신 메서드(method) 사용하기
② 사용하지 않는 생성자는 public 대신 protected로 바꾸기
⑤ varchar 대신 length 사용하기
package com.example.plan.comment7.service;
import com.example.plan.comment7.dto.response.CommentResponseDto;
import com.example.plan.comment7.entity.Comment;
import com.example.plan.comment7.repository.CommentRepository;
import com.example.plan.plan7.entity.Plan;
import com.example.plan.plan7.repository.PlanRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Service
public class CommentServiceImpl implements CommentService {
private final PlanRepository planRepository;
private final CommentRepository commentRepository;
@Transactional
@Override
public CommentResponseDto save(String content, Long planId) {
Plan foundplan = planRepository.findByIdOrElseThrow(planId);
Comment commentToSave = new Comment(content);
commentToSave.setPlan(foundplan);
commentToSave.setMember(foundplan.getMember());
Comment savedComment = commentRepository.save(commentToSave);
return CommentResponseDto.toDto(savedComment);
}
}
package com.example.plan.comment7.service;
import com.example.plan.comment7.dto.response.CommentResponseDto;
import com.example.plan.comment7.entity.Comments;
import com.example.plan.comment7.repository.CommentRepository;
import com.example.plan.plan7.entity.Plan;
import com.example.plan.plan7.repository.PlanRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
@RequiredArgsConstructor
@Service
public class CommentServiceImpl implements CommentService {
private final PlanRepository planRepository;
private final CommentRepository commentRepository;
@Transactional
@Override
public CommentResponseDto createComment(
String content,
Long planId
) {
Plan foundPlan = planRepository
.findByIdAndIsDeletedFalse(planId)
.orElseThrow(
() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Id does not exist"
)
);
Comments commentToSave = new Comments(content);
commentToSave.updatePlan(foundPlan);
commentToSave.updateMember(foundPlan.getMember());
Comments savedComment = commentRepository.save(commentToSave);
return CommentResponseDto.toDto(savedComment);
}
}
package com.example.plan.comment7.entity;
import com.example.plan.base.BaseEntity;
import com.example.plan.member7.entity.Member;
import com.example.plan.plan7.entity.Plan;
import jakarta.persistence.*;
import lombok.Getter;
import org.hibernate.annotations.Comment;
/*
[수정 전] Comment
[수정 후] Comments
[수정 이유] Comment 어노테이션(annotation, @Comment)과 구분
*/
@Getter
@Entity
@Table(name = "comments7")
public class Comments extends BaseEntity {
@Comment("댓글 식별자")
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "BIGINT")
private Long id;
@Comment("댓글 내용")
@Column(
name = "content",
length = 255
)
private String content;
@ManyToOne
@JoinColumn(
name = "plan_id",
nullable = false
)
private Plan plan;
@ManyToOne
@JoinColumn(
name = "member_id",
nullable = false
)
private Member member;
protected Comments() {
}
public Comments(String content) {
this.content = content;
}
public void updatePlan(Plan plan) {
this.plan = plan;
}
public void updateMember(Member member) {
this.member = member;
}
public void updateContent(String content) {
this.content = content;
}
}
③ JPQL을 전부 쿼리 메서드(query method)로 고치기
④ 예외 처리는 전부 서비스 레이어(Service Layer)로 옮기기
package com.example.plan.plan7.repository;
import com.example.plan.plan7.entity.Plan;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.util.Optional;
public interface PlanRepository extends JpaRepository<Plan, Long> {
@Query("SELECT p FROM Plan p WHERE p.isDeleted IS NULL")
Page<Plan> findAllExceptDeleted(Pageable pageable);
@Query("SELECT p FROM Plan p WHERE p.id = :id AND p.isDeleted IS NULL")
Optional<Plan> findByIdExceptDeleted(Long id);
default Plan findByIdOrElseThrow(Long id) {
return findByIdExceptDeleted(id).orElseThrow(
() -> new ResponseStatusException(
HttpStatus.NOT_FOUND
, "Id does not exist"
)
);
}
@Transactional
@Modifying
@Query(
"UPDATE Plan p " +
"SET p.isDeleted = true, p.deletedAt = CURRENT_TIMESTAMP " +
"WHERE p.id = :id " +
"AND p.isDeleted IS NULL"
)
int softDeleteById(Long id);
@Transactional
@Modifying
@Query(
"UPDATE Plan p " +
"SET p.isDeleted = TRUE, p.deletedAt = CURRENT_TIMESTAMP " +
"WHERE p.member.id = :id " +
"AND p.isDeleted IS NULL"
)
void softDeleteByMemberId(Long id);
}
package com.example.plan.plan7.repository;
import com.example.plan.plan7.entity.Plan;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface PlanRepository extends JpaRepository<Plan, Long> {
Page<Plan> findAllByIsDeletedFalse(Pageable pageable);
Optional<Plan> findByIdAndIsDeletedFalse(Long planId);
List<Plan> findAllByMemberIdAndIsDeletedFalse(Long memberId);
}
⑥ DTO를 모두 레코드 클래스(record class)로 바꾸기
⑦ 의미가 더 잘 드러나도록 변수 및 메서드(method) 이름 다듬기
package com.example.plan.plan7.dto.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import org.hibernate.validator.constraints.Length;
@Getter
public class CreatePlanRequestDto {
@NotBlank(message = "일정의 제목 입력은 필수입니다.")
@Length (min = 1, max = 20)
private final String title;
@NotEmpty(message = "null과 빈값을 허용하지 않습니다.")
@Length(max = 200)
private final String task;
@NotNull(message = "사용자 id 입력은 필수입니다.")
private final Long userId;
public CreatePlanRequestDto(
String title
, String task
, Long userId
) {
this.title = title;
this.task = task;
this.userId = userId;
}
}
package com.example.plan.plan7.dto.request;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
public record CreatePlanRequestDto(
@NotBlank(message = "일정의 제목 입력은 필수입니다.")
@Length(min = 1, max = 20)
String title,
@NotEmpty(message = "null과 빈값을 허용하지 않습니다.")
@Length(max = 200)
String task,
@NotNull(message = "사용자 id 입력은 필수입니다.")
Long memberId
) {
}
(4) [링크] 리팩토링(refactoring)을 진행할 때 참고한 글
3. 팀장 특강을 열어도 될까?
오늘 뉴스피드 프로젝트를 같이 진행한 팀원이 이번 프로젝트 때 팀장 해볼 용기를 내고 싶은데 잘할 수 있을지 고민된다고 얘기했다. 이런저런 이야기를 나누면서 만약 자신이 정말 팀장이 된다면 종종 질문하겠다고 할 때 문득 '팀장 특강을 열면 괜찮으려나?' 싶은 생각이 머릿속을 스쳤다. 특강이란 거창한 표현을 썼지만 막상 열었을 때 한두 명만 오고 말 수도 있었다. 그건 별로 중요하지 않았다. '몇 명이 들으러 왔는가?'보다는 '한 사람이라도 이 특강을 원했다'는 사실이 훨씬 더 중요했다.
'그냥 질문받을 때마다 대답해 주면 그만 아닌가?'
물론 질문할 때마다 메시지로 대답해도 상관은 없겠지만, 팀원이 용기를 낸 만큼 공들여 대답해 주고 싶었다. 서로 이야기를 나누면서 나 또한 무언가를 배울 수도 있었다. 팀원이 용기를 낸 만큼, 정성을 가득 담아 대답해 주고 싶었다.
때마침 팀장 관련 책도 이번 주에 다 읽을 예정인지라, 다음 주 월요일 오후나 저녁에 일정을 잡으면 좋지 않을까 싶다. '팀장'이라고는 말하나 사실은 조장이고, 그저 팀 프로젝트 때마다 팀장을 했을 뿐인데. 그 횟수도 딱 두 번이고.
팀원이 진지하게 물어봤으니, 팀장도 그만큼 진지하게 대답해 줘야 한다.
우선은 이번 주에 과제 마치는 속도를 전보다 더 높여 봐야겠다.
'끝을 보는 용기' 카테고리의 다른 글
Day 088 - Spring 심화 프로젝트 3단계 진행 중, 팀 프로젝트 전에 다 같이 코드 작성 규칙을 정하다. (0) | 2025.01.02 |
---|---|
Day 087 - 심화 Spring 3주차 완강, Spring 심화 프로젝트 3단계 진행 중, 지금 눈앞에 있는 코드는 꿈일 거야 (0) | 2025.01.01 |
Day 085 - 심화 Spring 1주차 완강, 눈에 띄는 기술 블로그를 운영하려면 어떤 글을 써야 좋을까? (0) | 2024.12.30 |
깊은 애도를 표합니다. (0) | 2024.12.29 |
Day 084 - 두 번째 다면평가 (0) | 2024.12.29 |