Skip to content

munhyerin22/spring-plus

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

22 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌱 Spring Plus

Spring Boot 기반의 일정 관리 λ°±μ—”λ“œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€.
JWT 인증, JPA, QueryDSL, Spring Security λ“± λ‹€μ–‘ν•œ 기술 μŠ€νƒμ„ ν™œμš©ν•˜μ—¬ 개발.


πŸ›  기술 μŠ€νƒ

λΆ„λ₯˜ 기술
Language Java 17
Framework Spring Boot 3.3.3
ORM Spring Data JPA, QueryDSL
Security Spring Security, JWT (jjwt 0.11.5)
DB MySQL
Build Gradle
μ•”ν˜Έν™” BCrypt

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰

src/main/java/org/example/expert
β”œβ”€β”€ aop/                  # AOP κ΄€λ ¨
β”œβ”€β”€ client/               # μ™ΈλΆ€ API ν΄λΌμ΄μ–ΈνŠΈ (날씨)
β”œβ”€β”€ config/               # μ„€μ • (JWT, Filter, Security λ“±)
β”œβ”€β”€ domain/
β”‚   β”œβ”€β”€ auth/             # 인증 (νšŒμ›κ°€μž…, 둜그인)
β”‚   β”œβ”€β”€ comment/          # λŒ“κΈ€
β”‚   β”œβ”€β”€ common/           # 곡톡 (μ˜ˆμ™Έ, DTO, μ–΄λ…Έν…Œμ΄μ…˜)
β”‚   β”œβ”€β”€ manager/          # λ‹΄λ‹Ήμž
β”‚   β”œβ”€β”€ todo/             # 일정
β”‚   └── user/             # μœ μ €

βœ… κ΅¬ν˜„ κΈ°λŠ₯

Level 1

1. @Transactional 이해 - ν•  일 μ €μž₯ 였λ₯˜ μˆ˜μ •

  • TodoService의 saveTodo() λ©”μ„œλ“œμ— @Transactional이 λˆ„λ½λ˜μ–΄ read-only νŠΈλžœμž­μ…˜μ—μ„œ INSERTκ°€ μ‹€νŒ¨ν•˜λ˜ 문제λ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.
  • 클래슀 레벨의 @Transactional(readOnly = true) μ•„λž˜μ— λ©”μ„œλ“œ 레벨둜 @Transactional을 μΆ”κ°€ν•˜μ—¬ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.

2. JWT에 λ‹‰λ„€μž„ μΆ”κ°€

  • User 엔티티에 nickname μ»¬λŸΌμ„ μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.
  • νšŒμ›κ°€μž… μš”μ²­(SignupRequest) 및 응닡 DTO에 λ‹‰λ„€μž„ ν•„λ“œλ₯Ό λ°˜μ˜ν–ˆμŠ΅λ‹ˆλ‹€.
  • JWT 토큰 생성 μ‹œ nickname claim을 ν¬ν•¨ν•˜λ„λ‘ JwtUtil을 μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.

3. JPQL 기반 쑰건 검색 μΆ”κ°€

  • ν•  일 λͺ©λ‘ 쑰회 μ‹œ weather와 μˆ˜μ •μΌ(modifiedAt) λ²”μœ„λ₯Ό μ„ νƒμ μœΌλ‘œ 필터링할 수 μžˆλ„λ‘ κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€.
  • JPQL을 μ‚¬μš©ν•˜λ©°, 각 쑰건은 null이면 λ¬΄μ‹œλ©λ‹ˆλ‹€.
// μ˜ˆμ‹œ: weather + κΈ°κ°„ 쑰건 검색
GET /todos?weather=Sunny&startDate=2024-01-01T00:00:00&endDate=2024-12-31T23:59:59

4. 컨트둀러 ν…ŒμŠ€νŠΈ μˆ˜μ •

  • todo_단건_쑰회_μ‹œ_todoκ°€_μ‘΄μž¬ν•˜μ§€_μ•Šμ•„_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() ν…ŒμŠ€νŠΈκ°€ μ‹€νŒ¨ν•˜λŠ” 원인은, InvalidRequestException λ°œμƒ μ‹œ μƒνƒœμ½”λ“œκ°€ 400 BAD_REQUESTμž„μ—λ„ ν…ŒμŠ€νŠΈμ—μ„œ 200 OKλ₯Ό κΈ°λŒ€ν•˜κ³  μžˆμ—ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.
  • ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μ•„λž˜μ™€ 같이 μˆ˜μ •ν•˜μ—¬ ν†΅κ³Όμ‹œμΌ°μŠ΅λ‹ˆλ‹€.
mockMvc.perform(get("/todos/{todoId}", todoId))
    .andExpect(status().isBadRequest())
    .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.name()))
    .andExpect(jsonPath("$.code").value(HttpStatus.BAD_REQUEST.value()))
    .andExpect(jsonPath("$.message").value("Todo not found"));

5. AOP μˆ˜μ •

  • AdminAccessLoggingAspect의 포인트컷이 UserController.getUser()λ₯Ό 가리킀고 μžˆμ—ˆμœΌλ‚˜, μ˜λ„λŠ” UserAdminController.changeUserRole() μ‹€ν–‰ μ „ λ‘œκΉ…μ΄μ—ˆμŠ΅λ‹ˆλ‹€.
  • μ–΄λ“œλ°”μ΄μŠ€λ₯Ό @After β†’ @Before둜, ν¬μΈνŠΈμ»·μ„ changeUserRole()둜 λ³€κ²½ν–ˆμŠ΅λ‹ˆλ‹€.
@Before("execution(* org.example.expert.domain.user.controller.UserAdminController.changeUserRole(..))")
public void logBeforeChangeUserRole(JoinPoint joinPoint) { ... }

Level 2

6. JPA Cascade - λ‹΄λ‹Ήμž μžλ™ 등둝

  • Todo 생성 μ‹œ μž‘μ„±μžκ°€ λ‹΄λ‹Ήμž(Manager)둜 μžλ™ λ“±λ‘λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
  • Todo μ—”ν‹°ν‹°μ˜ managers μ»¬λ ‰μ…˜μ— cascade = CascadeType.PERSIST μ˜΅μ…˜μ„ μΆ”κ°€ν•˜μ—¬, Todo μ €μž₯ μ‹œ Manager도 ν•¨κ»˜ μ €μž₯λ˜λ„λ‘ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€.
@OneToMany(mappedBy = "todo", cascade = CascadeType.PERSIST)
private List<Manager> managers = new ArrayList<>();

7. N+1 문제 ν•΄κ²° - λŒ“κΈ€ 쑰회

  • CommentRepository.findByTodoIdWithUser()의 JPQL을 JOIN FETCH둜 μˆ˜μ •ν•˜μ—¬ μ—°κ΄€λœ Userλ₯Ό ν•œ 번의 쿼리둜 κ°€μ Έμ˜€λ„λ‘ κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€.
@Query("SELECT c FROM Comment c JOIN FETCH c.user WHERE c.todo.id = :todoId")
List<Comment> findByTodoIdWithUser(@Param("todoId") Long todoId);

8. QueryDSL μ „ν™˜ - Todo 단건 쑰회

  • JPQL둜 μž‘μ„±λœ findByIdWithUser()λ₯Ό QueryDSL둜 μ „ν™˜ν–ˆμŠ΅λ‹ˆλ‹€.
  • fetchJoin()을 μ‚¬μš©ν•˜μ—¬ N+1 λ¬Έμ œκ°€ λ°œμƒν•˜μ§€ μ•Šλ„λ‘ μ²˜λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.
return queryFactory
    .selectFrom(todo)
    .leftJoin(todo.user, user).fetchJoin()
    .where(todo.id.eq(todoId))
    .fetchOne();

Level 3 (도전)

10. QueryDSL + Projection 기반 검색 API

  • μƒˆλ‘œμš΄ API GET /todos/searchλ₯Ό μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€.
  • 검색 쑰건: 제λͺ©(λΆ€λΆ„ 일치), 생성일 λ²”μœ„, λ‹΄λ‹Ήμž λ‹‰λ„€μž„(λΆ€λΆ„ 일치)
  • Projections.constructorλ₯Ό ν™œμš©ν•˜μ—¬ ν•„μš”ν•œ ν•„λ“œ(제λͺ©, λ‹΄λ‹Ήμž 수, λŒ“κΈ€ 수)만 λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • κ²°κ³ΌλŠ” νŽ˜μ΄μ§• μ²˜λ¦¬λ˜μ–΄ λ°˜ν™˜λ©λ‹ˆλ‹€.
// 응닡 μ˜ˆμ‹œ
{
  "title": "μŠ€ν”„λ§ 곡뢀",
  "managerCount": 3,
  "commentCount": 5
}

11. Transaction 심화 - λ§€λ‹ˆμ € 등둝 둜그 ----> κ΅¬ν˜„ μ˜ˆμ •

  • λ§€λ‹ˆμ € 등둝 μš”μ²­ μ‹œ log ν…Œμ΄λΈ”μ— μš”μ²­ 둜그λ₯Ό μ €μž₯ν•©λ‹ˆλ‹€.
  • @Transactional(propagation = Propagation.REQUIRES_NEW)λ₯Ό μ‚¬μš©ν•˜μ—¬ λ§€λ‹ˆμ € 등둝 νŠΈλžœμž­μ…˜κ³Ό 둜그 μ €μž₯ νŠΈλžœμž­μ…˜μ„ λΆ„λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.
  • λ§€λ‹ˆμ € 등둝이 μ‹€νŒ¨ν•˜λ”λΌλ„ λ‘œκ·ΈλŠ” λ°˜λ“œμ‹œ μ €μž₯λ©λ‹ˆλ‹€.

πŸ”‘ API λͺ…μ„Έ

Auth

Method URI μ„€λͺ…
POST /auth/signup νšŒμ›κ°€μž…
POST /auth/signin 둜그인

User

Method URI μ„€λͺ… κΆŒν•œ
GET /users/{userId} μœ μ € 쑰회 인증
PUT /users λΉ„λ°€λ²ˆν˜Έ λ³€κ²½ 인증
PATCH /admin/users/{userId} μœ μ € μ—­ν•  λ³€κ²½ ADMIN

Todo

Method URI μ„€λͺ… κΆŒν•œ
POST /todos 일정 생성 인증
GET /todos 일정 λͺ©λ‘ 쑰회 (νŽ˜μ΄μ§•) 인증
GET /todos/{todoId} 일정 단건 쑰회 인증
GET /todos/search 일정 검색 (QueryDSL) 인증

Comment

Method URI μ„€λͺ… κΆŒν•œ
POST /todos/{todoId}/comments λŒ“κΈ€ 생성 인증
GET /todos/{todoId}/comments λŒ“κΈ€ λͺ©λ‘ 쑰회 인증

Manager

Method URI μ„€λͺ… κΆŒν•œ
POST /todos/{todoId}/managers λ‹΄λ‹Ήμž 등둝 인증
GET /todos/{todoId}/managers λ‹΄λ‹Ήμž λͺ©λ‘ 쑰회 인증
DELETE /todos/{todoId}/managers/{managerId} λ‹΄λ‹Ήμž μ‚­μ œ 인증

πŸ“ μ½”λ“œκ°œμ„ 

@Transactional readOnly와 μ“°κΈ° μž‘μ—…

  • 문제: 클래슀 λ ˆλ²¨μ— @Transactional(readOnly = true)κ°€ μ„ μ–Έλœ μƒνƒœμ—μ„œ saveTodo()에 별도 @Transactional이 μ—†μ–΄ INSERT 쿼리 μ‹€ν–‰ μ‹œ μ˜ˆμ™Έ λ°œμƒ
  • ν•΄κ²°: μ“°κΈ° μž‘μ—…μ΄ ν•„μš”ν•œ λ©”μ„œλ“œμ— @Transactional을 λͺ…μ‹œμ μœΌλ‘œ μΆ”κ°€

AOP 포인트컷 였λ₯˜

  • 문제: @After μ–΄λ“œλ°”μ΄μŠ€κ°€ 잘λͺ»λœ λŒ€μƒ λ©”μ„œλ“œλ₯Ό 가리킀고 μžˆμ–΄ μ˜λ„ν•œ λ™μž‘ λΆˆκ°€
  • ν•΄κ²°: 포인트컷 λŒ€μƒκ³Ό μ–΄λ“œλ°”μ΄μŠ€ μ’…λ₯˜(@Before)λ₯Ό λͺ¨λ‘ μˆ˜μ •

N+1 문제

  • 문제: λŒ“κΈ€ 쑰회 μ‹œ 각 λŒ“κΈ€λ§ˆλ‹€ μœ μ €λ₯Ό 별도 쿼리둜 μ‘°νšŒν•˜μ—¬ N+1 λ°œμƒ
  • ν•΄κ²°: JOIN FETCH둜 μ—°κ΄€ μ—”ν‹°ν‹°λ₯Ό ν•œ λ²ˆμ— 쑰회

About

spring-plus

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Java 100.0%