๐Ÿ“ GitHub ๋ฐ”๋กœ๊ฐ€๊ธฐ

Project Intro

1๏ธโƒฃ ์„œ๋น„์Šค ์š”๊ตฌ์‚ฌํ•ญ

API ๊ตฌ๋ถ„๊ธฐ๋Šฅ
์ „์ฒด๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์กฐํšŒ- ์ œ๋ชฉ, ์ž‘์„ฑ์ž๋ช…, ์ž‘์„ฑ๋‚ด์šฉ, ์ž‘์„ฑ๋‚ ์งœ ์กฐํšŒ
- ์ž‘์„ฑ๋‚ ์งœ ๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ
๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ- ์ œ๋ชฉ, ์ž‘์„ฑ์ž๋ช…, ๋น„๋ฐ€๋ฒˆํ˜ธ, ์ž‘์„ฑ๋‚ด์šฉ ์ €์žฅ
- ์ €์žฅ๋œ๊ฒŒ์‹œ๊ธ€ Client ๋ฐ˜ํ™˜
์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ- ์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€ ์ œ๋ชฉ, ์ž‘์„ฑ์ž๋ช…, ์ž‘์„ฑ๋‚ ์งœ, ์ž‘์„ฑ๋‚ด์šฉ ์กฐํšŒ
์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ •- ์ˆ˜์ •์š”์ฒญ ์‹œ, ์ˆ˜์ •ํ• ๋ฐ์ดํ„ฐ + ๋น„๋ฐ€๋ฒˆํ˜ธ โ†’ ์„œ๋ฒ„์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ๊ฒ€์‚ฌ
- ์ œ๋ชฉ, ์ž‘์„ฑ์ž๋ช…, ์ž‘์„ฑ๋‚ด์šฉ ์ˆ˜์ •
- ์ˆ˜์ •๋œ๊ฒŒ์‹œ๊ธ€ Client ๋ฐ˜ํ™˜
์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ- ์‚ญ์ œ์š”์ฒญ ์‹œ, ๋น„๋ฐ€๋ฒˆํ˜ธ โ†’ ์„œ๋ฒ„์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ๊ฒ€์‚ฌ
- ์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œ
- ์„ฑ๊ณต๋ฉ”์‹œ์ง€ Client ๋ฐ˜ํ™˜

2๏ธโƒฃ Service Architecture

3๏ธโƒฃ Usecase

4๏ธโƒฃ API ๋ช…์„ธ์„œ

๊ธฐ๋ŠฅMethodURLRequestResponse
๊ฒŒ์‹œ๊ธ€์ž‘์„ฑPOST/post{
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "password" : "๋น„๋ฐ€๋ฒˆํ˜ธ",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ"
}
{
ย ย "id" : "๊ณ ์œ ๋ฒˆํ˜ธ",
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ",
ย ย "createdAt" : "์ƒ์„ฑ์‹œ๊ฐ„",
ย ย "modifiedAt" : "์ˆ˜์ •์‹œ๊ฐ„"
}
์ „์ฒด๊ฒŒ์‹œ๊ธ€๋ชฉ๋ก์กฐํšŒGET/posts-[
{
ย ย "id" : "๊ณ ์œ ๋ฒˆํ˜ธ",
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ",
ย ย "createdAt" : "์ƒ์„ฑ์‹œ๊ฐ„",
ย ย "modifiedAt" : "์ˆ˜์ •์‹œ๊ฐ„"
},
{
ย ย "id" : "๊ณ ์œ ๋ฒˆํ˜ธ",
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ",
ย ย "createdAt" : "์ƒ์„ฑ์‹œ๊ฐ„",
ย ย "modifiedAt" : "์ˆ˜์ •์‹œ๊ฐ„"
}
]
์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€์กฐํšŒGET/post/{post-id}-{
ย ย "id" : "๊ณ ์œ ๋ฒˆํ˜ธ",
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ",
ย ย "createdAt" : "์ƒ์„ฑ์‹œ๊ฐ„",
ย ย "modifiedAt" : "์ˆ˜์ •์‹œ๊ฐ„"
}
์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€์ˆ˜์ •PUT/post/{post-id}{
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "password" : "๋น„๋ฐ€๋ฒˆํ˜ธ",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ"
}
{
ย ย "id" : "๊ณ ์œ ๋ฒˆํ˜ธ",
ย ย "title" : "์ œ๋ชฉ",
ย ย "username" : "์ž‘์„ฑ์ž๋ช…",
ย ย "content" : "์ž‘์„ฑ๋‚ด์šฉ",
ย ย "createdAt" : "์ƒ์„ฑ์‹œ๊ฐ„",
ย ย "modifiedAt" : "์ˆ˜์ •์‹œ๊ฐ„"
}
์„ ํƒํ•œ๊ฒŒ์‹œ๊ธ€์‚ญ์ œDELETE/post/{post-id}{
ย ย "password" : "๋น„๋ฐ€๋ฒˆํ˜ธ"
}
{
ย ย "status" : 200,
ย ย "message" : "Deleted Post Successfully"
}
{
ย ย "status" : ["The Post does not exist.", "Password is incorrect"]
}

1 Project Setting


2 Entity ์„ค๊ณ„

1๏ธโƒฃ Timestamped.java

@MappedSuperclass // ์กฐ์ƒํด๋ž˜์Šค (but, Table์„ค๊ณ„ X)
@Getter
@EntityListeners(AuditingEntityListener.class) // ํ•ด๋‹น Entity ๋ณ€ํ™”๊ฐ์ง€ -> ํ…Œ์ด๋ธ”๋ฐ์ดํ„ฐ์กฐ์ž‘
public class Timestamped {
    
    // ์ƒ์„ฑ์‹œ๊ฐ„
    @CreatedDate
    private LocalDateTime createdAt;
    
    // ์ˆ˜์ •์‹œ๊ฐ„
    @LastModifiedDate
    private LocalDateTime modifiedAt;
    
}
  • MallangLogApplication โ†’ @EnableJpaAuditing ์ถ”๊ฐ€ํ•„์ˆ˜

2๏ธโƒฃ Post.java

@Entity
@Getter
@NoArgsConstructor // ํŒŒ๋ผ๋ฏธํ„ฐ์—†๋Š” ๊ธฐ๋ณธ์ƒ์„ฑ์ž
public class Post extends Timestamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String content;

}

3 Dto ์„ค๊ณ„

1๏ธโƒฃ PostRequestDto.java

// Client ์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ์ •๋ณด๋ฅผ ๋‹ด๋Š” Dto
@Getter
public class PostRequestDto {

    private String title;

    private String username;

    private String password;

    private String content;

}

2๏ธโƒฃ Post.java โ†’ ์ƒ์„ฑ์ž(์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ)

// Client Request ๊ฐ’ -> Dto์—์„œ ๋ฐ›์•„์™€์„œ -> ๋ฎ์–ด์”Œ์›€
    @Builder // ๋นŒ๋”ํŒจํ„ด : ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ๋ฐ›์Œ (-> ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ)
    private Post(String title, String username, String password, String content) {
        this.title = title;
        this.username = username;
        this.password = password;
        this.content = content;
    }

    // ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ
    public static Post of(PostRequestDto postRequestDto) {
        return Post.builder()
                .title(postRequestDto.getTitle())
                .username(postRequestDto.getUsername())
                .password(postRequestDto.getPassword())
                .content(postRequestDto.getContent())
                .build();
    }

3๏ธโƒฃ PostResponseDto.java

// Post Entity ๊ฐ’(๋ชจ๋“ ๋ณ€๊ฒฝ์ด ๋๋‚จ) getter -> Dto ๋ฎ์–ด์”Œ์šด ํ›„ -> Client์— ๋ฐ˜ํ™˜
@Getter
public class PostResponseDto {

    private Long id;

    private String title;

    private String username;

    private String content;

    private LocalDateTime createdAt;

    private LocalDateTime modifiedAt;

}

4๏ธโƒฃ PostResponseDto.java โ†’ ์ƒ์„ฑ์ž(์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ)

// ์ƒ์„ฑ์ž
    @Builder
    private PostResponseDto(Long id,
                            String title, String username, String content,
                            LocalDateTime createdAt, LocalDateTime modifiedAt) {
        this.id = id;
        this.title = title;
        this.username = username;
        this.content = content;
        this.createdAt = createdAt;
        this.modifiedAt = modifiedAt;
    }

    // ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ
    public static PostResponseDto of(Post post) {
        return PostResponseDto.builder()
                .id(post.getId())
                .title(post.getTitle())
                .username(post.getUsername())
                .content(post.getContent())
                .createdAt(post.getCreatedAt())
                .modifiedAt(post.getModifiedAt())
                .build();
    }

4 Post์ƒ์„ฑ ์„ค๊ณ„

1๏ธโƒฃ PostController.java

@RestController // @Controller + @ResponseBody -> JSONํ˜•ํƒœ ๋ฐ์ดํ„ฐ๋ฐ˜ํ™˜
@RequiredArgsConstructor // final, @Notnull ํ•„๋“œ -> ์ƒ์„ฑ์ž์ƒ์„ฑ
public class PostController {

    private final PostService postService;

    // Post ์ƒ์„ฑ
    @PostMapping("/post")
    public PostResponseDto createPost(@RequestBody PostRequestDto postRequestDto) {
        return postService.createPost(postRequestDto);
    }
        // ๋Š์Šจํ•œ๊ฒฐํ•ฉ : Entity ๋ฐ”๋กœ ๋ฐ˜ํ™˜ X, Dto์— ๋‹ด์•„ ๋ฐ˜ํ™˜ !
        // @RequestBody : Client ์ž…๋ ฅ๊ฐ’ -> HTTP Body์— JSONํ˜•ํƒœ๋กœ ์ง€์ •๊ฐ์ฒด์— ๋‹ด์•„์˜ด

}

2๏ธโƒฃ PostService.java

@Service
@RequiredArgsConstructor // final, @Notnull ํ•„๋“œ -> ์ƒ์„ฑ์ž์ƒ์„ฑ
public class PostService {

    private final PostRepository postRepository;

    // Post ์ƒ์„ฑ
    @Transactional // DB์ฒ˜๋ฆฌ ์ž‘์—… ์ค‘ ์˜ค๋ฅ˜ -> ๋ชจ๋“ ์ž‘์—… ์›์ƒํƒœ๋กœ ๋ณต๊ตฌ
    public PostResponseDto createPost(PostRequestDto postRequestDto) {

        // Entity ๊ฐ์ฒด ์ƒ์„ฑ -> ์ •์ ํŒฉํ† ๋ฆฌ๋ฉ”์„œ๋“œ(์ƒ์„ฑ์ž) ์ดˆ๊ธฐํ™”
        Post post = Post.of(postRequestDto);

        // ์ดˆ๊ธฐํ™” ๋œ Entity ๊ฐ์ฒด -> Repository ์ €์žฅ
        postRepository.save(post);

        // ์ดˆ๊ธฐํ™” ๋œ Entity ๊ฐ์ฒด -> ResponseDto ์ƒ์„ฑ์ž์— ์ „๋‹ฌ
        return PostResponseDto.of(post);

    }

}

3๏ธโƒฃ Postman ํ™•์ธ

๐Ÿ“ application.properties H2์„ค์ •

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:db;MODE=MYSQL;
spring.datasource.username=mallang
spring.datasource.password=


5 Post์ „์ฒด์กฐํšŒ ์„ค๊ณ„

1๏ธโƒฃ PostController.java

<// Post ์ „์ฒด์กฐํšŒ
@GetMapping("/posts")
public List<PostResponseDto> getPosts() {
    return postService.getPosts();
}

2๏ธโƒฃ PostService.java

// Post ์ „์ฒด์กฐํšŒ
@Transactional(readOnly = true)
public List<PostResponseDto> getPosts() {

    // Repository -> Entity ๊ฐ์ฒด๋ฐฐ์—ด
    List<Post> postList = postRepository.findAllByOrderByModifiedAtDesc();

    // ResponseDto ๋ฐฐ์—ด ์ƒ์„ฑ
    List<PostResponseDto> postResponseDtoList = new ArrayList<>();

    // postList -> postResponseDtoList (List -> List๋กœ ๋ฐ”๋กœ ๋ชป ์˜ฎ๊น€)
    for (Post post : postList) {
       	postResponseDtoList.add(PostResponseDto.of(post));
    }

   return postResponseDtoList;

}

3๏ธโƒฃ PostRepository.java

// Post ์ „์ฒด์กฐํšŒ (์ž‘์„ฑ์‹œ๊ฐ„๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ)
    List<Post> findAllByOrderByModifiedAtDesc(); // ๋ฐ˜ํ™˜ํƒ€์ž… + ์กฐ๊ฑด

4๏ธโƒฃ Postman ํ™•์ธ


6 Post์„ ํƒ์กฐํšŒ ์„ค๊ณ„

1๏ธโƒฃ PostController.java

// Post ์„ ํƒ์กฐํšŒ
@GetMapping("/post/{id}")
public PostResponseDto getSelectedPost(@PathVariable Long id) {
    return postService.getSelectedPost(id);
}

2๏ธโƒฃ PoserService.java

// Post ์„ ํƒ์กฐํšŒ
@Transactional(readOnly = true)
public PostResponseDto getSelectedPost(Long id) {

    // Repository -> Entity ๊ฐ์ฒด ๋‹ด์•„์˜ด -> ์˜ˆ์™ธ์ฒ˜๋ฆฌ
    Post selectedPost = postRepository.findById(id).orElseThrow(
            // IllegalStateException : ์ ์ ˆํ•˜์ง€๋ชปํ•œ ์ธ์ž๋ฅผ ๋ฉ”์„œ๋“œ๋กœ ๋„˜๊ฒจ์ฃผ์—ˆ์„ ๋•Œ ์˜ˆ์™ธ
            () -> new IllegalStateException("The Post does not exist.")
    );

    // Entity ๊ฐ์ฒด์— ๋‹ด์•„์˜จ ๊ฒƒ -> ResponseDto ์ƒ์„ฑ์ž ์ดˆ๊ธฐํ™” -> Client ๋ฐ˜ํ™˜
    return PostResponseDto.of(selectedPost);

}

3๏ธโƒฃ Postman ํ™•์ธ

Id๊ฐ€ ์กด์žฌํ•  ๋•Œ

Id๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ


7 Post์ˆ˜์ • ์„ค๊ณ„

1๏ธโƒฃ PostController.java

// Post ์ˆ˜์ •
@PutMapping("/post/{id}")
public PostResponseDto updatePost(@PathVariable Long id,
                                  @RequestBody PostRequestDto postRequestDto) {
    return postService.updatePost(id, postRequestDto);
}

2๏ธโƒฃ PostService.java

// Post ์ˆ˜์ •
@Transactional
public PostResponseDto updatePost(Long id, PostRequestDto postRequestDto) {

    // Entity ๊ฐ์ฒด ์ƒ์„ฑ -> Repository์—์„œ id๋กœ ๋ถˆ๋Ÿฌ์˜ด -> ์˜ˆ์™ธ์ฒ˜๋ฆฌ
    Post updatePost = postRepository.findById(id).orElseThrow(
            () -> new IllegalStateException("The Post does not exist.")
    );

    // ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ๊ฒ€์‚ฌ
    if (! postRequestDto.getPassword().equals(updatePost.getPassword())) {
        throw new IllegalStateException("Password is incorrect");
    }

    // RequestDto -> ๋ถˆ๋Ÿฌ์˜จ Entity์— ๋ฎ์–ด์”Œ์›€
    updatePost.update(postRequestDto);

    // ์ˆ˜์ •์ด ๋๋‚œ ๋ถˆ๋Ÿฌ์˜จ Entity ๊ฐ์ฒด -> ResponseDto ์ƒ์„ฑ์ž ์ดˆ๊ธฐํ™” -> Client ๋ฐ˜ํ™˜
    return PostResponseDto.of(updatePost);

}

3๏ธโƒฃ Post.java

// Post ์ˆ˜์ • (Service -> RequestDto -> update ๋ฉ”์„œ๋“œ)
public void update(PostRequestDto postRequestDto) {
    this.title = postRequestDto.getTitle();
    this.content = postRequestDto.getContent();
}

4๏ธโƒฃ Postman ํ™•์ธ

Post ์ˆ˜์ •

์˜ˆ์™ธ์ฒ˜๋ฆฌ1 - Id๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ

์˜ˆ์™ธ์ฒ˜๋ฆฌ2 - ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ๊ฒ€์‚ฌ


8 Post์‚ญ์ œ ์„ค๊ณ„

1๏ธโƒฃ PostController.java

// Post ์‚ญ์ œ
@DeleteMapping("/post/{post-id}")
public Map<Integer, String> deletePost(@PathVariable(name="post-id") Long postId,
                                      @RequestBody PostRequestDto postRequestDto) {
    return postService.deletePost(postId, postRequestDto);
}

2๏ธโƒฃ PostService.java

// Post ์‚ญ์ œ
@Transactional
public Map<Integer, String> deletePost(Long postId, PostRequestDto postRequestDto) {

    // HashMap<key : value> -> ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ›„, ์ƒํƒœ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
    Map<Integer, String> statusMessage = new HashMap<>();

    // Entity ๊ฐ์ฒด ์ƒ์„ฑ -> Repository์—์„œ id๋กœ ๋ถˆ๋Ÿฌ์˜ด -> ์˜ˆ์™ธ์ฒ˜๋ฆฌ
    Post deletePost = postRepository.findById(postId).orElseThrow(
            () -> new IllegalStateException("The Post does not exist")
        );

    // ๋น„๋ฐ€๋ฒˆํ˜ธ ์œ ํšจ์„ฑ๊ฒ€์‚ฌ -> ์ƒํƒœ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
    if (postRequestDto.getPassword().equals(deletePost.getPassword())) {
        postRepository.deleteById(postId);
        statusMessage.put(200, "Deleted Post Successfully");
        return statusMessage;
    } else {
        statusMessage.put(500, "Password is incorrect");
        return statusMessage;
    }

}

3๏ธโƒฃ Postman ํ™•์ธ

Post์‚ญ์ œ ์„ฑ๊ณต๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜

Post์‚ญ์ œ ์‹คํŒจ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜ (๋น„๋ฐ€๋ฒˆํ˜ธ๋ถˆ์ผ์น˜)

profile
๐ŸฑSunyeon-Jeong, mallang developer๐Ÿฐ

0๊ฐœ์˜ ๋Œ“๊ธ€