Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/#6-seminar3-essential #7

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
.yml
*.yml
*.xml

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
.idea/dataSources.xml
.idea/inspectionProfiles/*
.idea/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
.yml
*.yml

### Eclipse ###
.apt_generated
Expand Down
9 changes: 9 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@ dependencies {

implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

// // ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์Šคํƒ€ํ„ฐ ์ถ”๊ฐ€
// implementation 'org.springframework.boot:spring-boot-starter-security'
// // ํƒ€์ž„๋ฆฌํ”„์—์„œ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์˜์กด์„ฑ ์ถ”๊ฐ€
// implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
// // ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ ์˜์กด์„ฑ ์ถ”๊ฐ€
// testImplementation 'org.springframework.security:spring-security-test'

runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java:8.0.32'

testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
Expand Down
72 changes: 66 additions & 6 deletions src/main/java/org/sopt/diary/api/controller/DiaryController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import org.sopt.diary.api.dto.response.DetailDiaryResponse;
import org.sopt.diary.api.dto.response.DiaryListResponse;
import org.sopt.diary.api.dto.response.DiaryResponse;
import org.sopt.diary.api.dto.response.ErrorCode;
import org.sopt.diary.api.exception.DiaryException;
import org.sopt.diary.domain.Diary;
import org.sopt.diary.domain.entity.DiaryEntity.Category;
import org.sopt.diary.service.DiaryService;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
Expand All @@ -26,34 +29,68 @@ public DiaryController(DiaryService diaryService) {
this.diaryService = diaryService;
}

// ์ผ๊ธฐ ์ž‘์„ฑ
@PostMapping("/diaries")
ResponseEntity<DiaryResponse> post(@Validated @RequestBody DiaryRequest diaryRequest) {
ResponseEntity<DiaryResponse> post(
@RequestHeader("Member-Id") Long memberId,
@Validated @RequestBody DiaryRequest diaryRequest
) {
// ๋‹ค์ด์–ด๋ฆฌ ์ƒ์„ฑ
long diaryId = diaryService.createDiary(diaryRequest.getTitle(), diaryRequest.getContent());
long diaryId = diaryService.createDiary(
diaryRequest.getTitle(),
diaryRequest.getContent(),
validateCreateCategory(diaryRequest.getCategory()),
diaryRequest.getIsPublic(),
memberId
);
return ResponseEntity.ok(new DiaryResponse(diaryId));
}

// ์ „์ฒด ์ผ๊ธฐ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ
@GetMapping("/diaries")
ResponseEntity<DiaryListResponse> getDiaryList() {
List<Diary> diaries = diaryService.getDiaries();
ResponseEntity<DiaryListResponse> getDiaryList(
@RequestParam(value = "category", required = false) String category
) {
List<Diary> diaries = diaryService.getDiaries(validateGetCategory(category));
return ResponseEntity.ok(DiaryListResponse.from(diaries));
}

// ํŠน์ • ์‚ฌ์šฉ์ž์˜ ์ผ๊ธฐ ๋ชฉ๋ก ์กฐํšŒ
@GetMapping("/diaries/me")
ResponseEntity<DiaryListResponse> getMyDiaryList(
@RequestHeader("Member-Id") Long memberId,
@RequestParam(value = "category", required = false) String category
) {
List<Diary> diaries = diaryService.getMyDiaries(memberId, validateGetCategory(category));
return ResponseEntity.ok(DiaryListResponse.from(diaries));
}

// ์ผ๊ธฐ ์ƒ์„ธ ์กฐํšŒ
@GetMapping("/diaries/{diaryId}")
ResponseEntity<DetailDiaryResponse> getDiary(@PathVariable long diaryId) {
ResponseEntity<DetailDiaryResponse> getDiary(
@PathVariable @Min(value = 1L, message = "๋‹ค์ด์–ด๋ฆฌ ์‹๋ณ„์ž๋Š” ์–‘์ˆ˜๋กœ ์ด๋ฃจ์–ด์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") long diaryId
) {
Diary diary = diaryService.getDiaryDetailById(diaryId);
return ResponseEntity.ok(DetailDiaryResponse.from(diary));
}

// ์ผ๊ธฐ ์ˆ˜์ •
@PatchMapping("/diaries/{diaryId}")
ResponseEntity<DetailDiaryResponse> updateDiary(
@PathVariable @Min(value = 1L, message = "๋‹ค์ด์–ด๋ฆฌ ์‹๋ณ„์ž๋Š” ์–‘์ˆ˜๋กœ ์ด๋ฃจ์–ด์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") long diaryId,
@RequestHeader("Member-Id") Long memberId,
@Validated @RequestBody DiaryRequest diaryRequest)
{
Diary updateDiary = diaryService.updateDiary(diaryId, diaryRequest.getTitle(), diaryRequest.getContent());
Diary updateDiary = diaryService.updateDiary(
diaryId,
diaryRequest.getTitle(),
diaryRequest.getContent(),
memberId
);
return ResponseEntity.ok(DetailDiaryResponse.from(updateDiary));
}

// ์ผ๊ธฐ ์‚ญ์ œ
@DeleteMapping("/diaries/{diaryId}")
ResponseEntity<DiaryResponse> deleteDiary(
@PathVariable @Min(value = 1L, message = "๋‹ค์ด์–ด๋ฆฌ ์‹๋ณ„์ž๋Š” ์–‘์ˆ˜๋กœ ์ด๋ฃจ์–ด์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.") long diaryId
Expand All @@ -62,5 +99,28 @@ ResponseEntity<DiaryResponse> deleteDiary(
diaryService.deleteDiary(diaryId);
return ResponseEntity.ok().build();
}

// ์นดํ…Œ๊ณ ๋ฆฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฉ”์„œ๋“œ (POST์—์„œ ์‚ฌ์šฉ, null ๋ถˆ๊ฐ€)
private Category validateCreateCategory(String category) {
if (category == null) {
throw new DiaryException(ErrorCode.INVALID_INPUT_CATEGORY_VALUE);
}
return validateCategory(category);
}

// ์นดํ…Œ๊ณ ๋ฆฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฉ”์„œ๋“œ (GET์—์„œ ์‚ฌ์šฉ, null ํ—ˆ์šฉ)
private Category validateGetCategory(String category) {
if (category == null) return null;
return validateCategory(category);
}

// ์นดํ…Œ๊ณ ๋ฆฌ ๊ฐ’์„ ๊ฒ€์ฆํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ
private Category validateCategory(String categoryName) {
try {
return Category.valueOf(categoryName.toUpperCase());
} catch (IllegalArgumentException e) {
throw new DiaryException(ErrorCode.CATEGORY_NOT_FOUND);
}
}
}

38 changes: 38 additions & 0 deletions src/main/java/org/sopt/diary/api/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.sopt.diary.api.controller;

import jakarta.validation.Valid;
import org.sopt.diary.api.dto.request.LoginRequest;
import org.sopt.diary.api.dto.request.SignupRequest;
import org.sopt.diary.api.dto.response.MemberResponse;
import org.sopt.diary.service.MemberService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/luckybicky")
public class MemberController {
private final MemberService memberService;

public MemberController(MemberService memberService) {
this.memberService = memberService;
}

@PostMapping("/signup")
public ResponseEntity<MemberResponse> signup(
@Valid @RequestBody SignupRequest memberRequest
) {
Long memberId = memberService.signupUser(memberRequest.getUsername(), memberRequest.getPassword(), memberRequest.getNickname(), memberRequest.getAge());
return ResponseEntity.ok(new MemberResponse(memberId));
}

@PostMapping("/login")
public ResponseEntity<MemberResponse> login(
@Valid @RequestBody LoginRequest memberRequest
) {
Long memberId = memberService.loginUser(memberRequest.getUsername(), memberRequest.getPassword());
return ResponseEntity.ok(new MemberResponse(memberId));
}
}
24 changes: 13 additions & 11 deletions src/main/java/org/sopt/diary/api/dto/request/DiaryRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,25 @@

public class DiaryRequest {
@NotBlank(message = "์ œ๋ชฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
@Size(max = 30, message = "์ œ๋ชฉ์€ 30์ž๋ฅผ ์ดˆ๊ณผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
@Size(max = 10, message = "์ œ๋ชฉ์€ 10์ž๋ฅผ ์ดˆ๊ณผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
private final String title;

@NotBlank(message = "๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
@Size(max = 100, message = "๋‚ด์šฉ์€ 100์ž๋ฅผ ์ดˆ๊ณผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
@Size(max = 30, message = "๋‚ด์šฉ์€ 30์ž๋ฅผ ์ดˆ๊ณผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
private final String content;

public DiaryRequest(String title, String content) {
private final String category;
private final Boolean isPublic;

public DiaryRequest(String title, String content, String category, Boolean isPublic) {
this.title = title;
this.content = content;
this.category = category;
this.isPublic = isPublic;
}

public String getTitle() {
return title;
}

public String getContent() {
return content;
}
}
public String getTitle() { return title; }
public String getContent() { return content; }
public String getCategory() { return category; }
public Boolean getIsPublic() { return isPublic; }
}
19 changes: 19 additions & 0 deletions src/main/java/org/sopt/diary/api/dto/request/LoginRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.sopt.diary.api.dto.request;

import jakarta.validation.constraints.NotBlank;

public class LoginRequest {
@NotBlank(message = "์œ ์ €์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
private final String username;

@NotBlank(message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
private final String password;

public LoginRequest(String username, String password) {
this.username = username;
this.password = password;
}

public String getUsername() { return username; }
public String getPassword() { return password; }
}
30 changes: 30 additions & 0 deletions src/main/java/org/sopt/diary/api/dto/request/SignupRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.sopt.diary.api.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public class SignupRequest {
@NotBlank(message = "์œ ์ €์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
private final String username;

@NotBlank(message = "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
private final String password;

@NotBlank(message = "๋‹‰๋„ค์ž„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
private final String nickname;

@NotNull(message = "๋‚˜์ด๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.")
private final Integer age;

public SignupRequest(String username, String password, String nickname, Integer age) {
this.username = username;
this.password = password;
this.nickname = nickname;
this.age = age;
}

public String getUsername() { return username; }
public String getPassword() { return password; }
public String getNickname() { return nickname; }
public Integer getAge() { return age; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
๋‹ค์ด์–ด๋ฆฌ ์ƒ์„ธ ์กฐํšŒ ์‹œ ํ•„์š”ํ•œ ๋ชจ๋“  ์ •๋ณด(id, title, content, createdAt, updatedAt)๋ฅผ ์ œ๊ณตํ•˜๋Š” DTO
*/
@JsonInclude(JsonInclude.Include.NON_NULL) // null์ธ ํ•„๋“œ๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Œ
public record DetailDiaryResponse(long id, String title, String content, String createdAt, String updatedAt) {
public record DetailDiaryResponse(
long id, String title, String content, String createdAt, String updatedAt, String category, String nickname
) {

public static DetailDiaryResponse from(Diary diary) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd-HH:mm:ss");
Expand All @@ -18,7 +20,9 @@ public static DetailDiaryResponse from(Diary diary) {
diary.getTitle(),
diary.getContent(),
diary.getCreatedAt().format(formatter),
diary.getUpdatedAt() != null ? diary.getUpdatedAt().format(formatter) : null
diary.getUpdatedAt() != null ? diary.getUpdatedAt().format(formatter) : null,
diary.getCategory(),
diary.getNickname()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public record DiaryListResponse(List<DiaryResponse> diaries) {
// Diary ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ›์•„ DiaryResponse ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜
public static DiaryListResponse from(List<Diary> diaries) {
List<DiaryResponse> diaryResponses = diaries.stream()
.map(diary -> new DiaryResponse(diary.getId(), diary.getTitle()))
.map(diary -> new DiaryResponse(diary.getId(), diary.getTitle(), diary.getCreatedAt(), diary.getNickname()))
.collect(Collectors.toList());
return new DiaryListResponse(diaryResponses);
}
Expand Down
21 changes: 18 additions & 3 deletions src/main/java/org/sopt/diary/api/dto/response/DiaryResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import org.sopt.diary.domain.entity.DiaryEntity;

import java.time.LocalDateTime;

/*
๋‹ค์ด์–ด๋ฆฌ ๋ชฉ๋ก ์กฐํšŒ ์‹œ ํ•„์š”ํ•œ ์ •๋ณด(id, title)๋ฅผ ์ œ๊ณตํ•˜๋Š” DTO
๋‹ค์ด์–ด๋ฆฌ ๋ชฉ๋ก ์กฐํšŒ ์‹œ ํ•„์š”ํ•œ ์ •๋ณด(id, title, createdAt, nickname)๋ฅผ ์ œ๊ณตํ•˜๋Š” DTO
*/
@JsonInclude(JsonInclude.Include.NON_NULL) // null์ธ ํ•„๋“œ๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Œ
public class DiaryResponse {
private final long id;
private String title;
private LocalDateTime createdAt;
private String nickname;

// id๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ƒ์„ฑ์ž
public DiaryResponse(long id) {
Expand All @@ -22,18 +26,29 @@ public DiaryResponse(long id, String title) {
this.title = title;
}

// id์™€ title, createdAt, nickname ์„ ๋ชจ๋‘ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ƒ์„ฑ์ž
public DiaryResponse(long id, String title, LocalDateTime createdAt, String nickname) {
this.id = id;
this.title = title;
this.createdAt = createdAt;
this.nickname = nickname;
}

public static DiaryResponse from(DiaryEntity diaryEntity) {
return new DiaryResponse(
diaryEntity.getId(),
diaryEntity.getTitle()
diaryEntity.getTitle(),
diaryEntity.getCreatedAt(),
diaryEntity.getNickname()
);
}

public long getId() {
return id;
}

public String getTitle() {
return title;
}
public LocalDateTime getCreatedAt() { return createdAt; }
public String getNickname() { return nickname; }
}
Loading