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

1 Service Architecture


2 Project API

๊ธฐ๋ŠฅMethodURLReturn
๋ฉ”์ธํŽ˜์ด์ง€GET/index.html
๋ฉ”๋ชจ ์ƒ์„ฑPOST/api/memosMemo
๋ฉ”๋ชจ ์กฐํšŒGET/api/memosList<Memo>
๋ฉ”๋ชจ ๋ณ€๊ฒฝPUT/api/memos/{id}Long
๋ฉ”๋ชจ ์‚ญ์ œDELETE/api/memos/{id}Long

3 Project setting

  • Spring Boot DevTools : ์ฝ”๋“œ์ˆ˜์ • โ†’ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•ด์•ผ ๋˜๋Š” ๋ถˆํŽธํ•œ ์ ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณต๋˜๋Š” ๋ชจ๋“ˆ

4 Entity ์„ค๊ณ„

1๏ธโƒฃ Timestamped

๐Ÿ“ main > entity > Timestamped.java

@Getter
@MappedSuperclass // ์กฐ์ƒํด๋ž˜์Šค
// @EntityListeners -> Application์— @EnableJpaAuditing ์ถ”๊ฐ€ํ•„์ˆ˜
@EntityListeners(AuditingEntityListener.class)
public class Timestamped {
    // ์ƒ์„ฑ์‹œ๊ฐ„
    @CreatedDate
    private LocalDateTime createdAt;

    // ์ˆ˜์ •์‹œ๊ฐ„
    @LastModifiedDate
    private LocalDateTime modifiedAt;
}

2๏ธโƒฃ Memo

๐Ÿ“ main > entity > Memo.java

@Getter
@Entity
@NoArgsConstructor // ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€
public class Memo extends Timestamped {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String contents;
}

5 Repository ์„ค๊ณ„

๐Ÿ“ main > repository > MemoRepository.java

public interface MemoRepository extends JpaRepository<Memo, Long> {
}

6 RequestDto ์„ค๊ณ„

๐Ÿ“ main > dto > MemoRequestDto

@Getter
public class MemoRequestDto {
    private String username;
    private String contents;
}

7 Memo์ƒ์„ฑ ์„ค๊ณ„

1๏ธโƒฃ Controller

๐Ÿ“ main > controller > MemoController.java

@RestController // @Controller + @ResponseBody -> Jsonํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฐ˜ํ™˜
@RequiredArgsConstructor // final, @Notnull -> ์ƒ์„ฑ์ž ์ž๋™์ƒ์„ฑ
public class MemoController {
    // service๋‹จ ๊ฐ์ฒด ๋ถˆ๋Ÿฌ์˜ด
    private final MemoService memoService;

    // ๋ฉ”์ธํŽ˜์ด์ง€ - html ๋ฐ˜ํ™˜
    @GetMapping("/")
    public ModelAndView home() {
        return new ModelAndView("index");
    }

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    @PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto memoRequestDto) {
        return memoService.createMemo(memoRequestDto);
    }
}

2๏ธโƒฃ Service

๐Ÿ“ main > service > MemoService.java

@Service
@RequiredArgsConstructor // final, @Notnull -> ์ƒ์„ฑ์ž ์ž๋™์ƒ์„ฑ
public class MemoService {
    private final MemoRepository memoRepository;

    // DB ์ž‘์—…์ฒ˜๋ฆฌ ์ค‘, ์˜ค๋ฅ˜๋ฐœ์ƒ -> ๋ชจ๋“  ์ž‘์—… ์›์ƒํƒœ๋กœ ๋Œ๋ฆผ
    @Transactional
    public Memo createMemo(MemoRequestDto memoRequestDto) {
        // Memo : ๋ฐ˜ํ™˜ํƒ€์ž…(Memo ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฐ˜ํ™˜)
        Memo memo = new Memo(memoRequestDto);
        memoRepository.save(memo);
        return memo;
    }
}

3๏ธโƒฃ Entity ์ˆ˜์ •

๐Ÿ“ main > entity > Memo.java

  • MemoRequestDto โ†’ Client ์ž…๋ ฅ๊ฐ’ ์ƒ์„ฑ์ž๋กœ ๋ฎ์–ด์”Œ์›€
@Getter
@Entity
@NoArgsConstructor // ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€
public class Memo extends Timestamped {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String contents;

    // MemoRequestDto -> ๊ฐ’์ด ๋ฎ์–ด์”Œ์›Œ์ง
    public Memo(MemoRequestDto memoRequestDto) {
        this.username = memoRequestDto.getUsername();
        this.contents = memoRequestDto.getContents();
    }
}

4๏ธโƒฃ H2-console, Postman ํ™•์ธ


8 Memo์กฐํšŒ ์„ค๊ณ„

1๏ธโƒฃ Controller

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

    // service๋‹จ ๊ฐ์ฒด ๋ถˆ๋Ÿฌ์˜ด
    private final MemoService memoService;

    // ๋ฉ”์ธํŽ˜์ด์ง€ - html ๋ฐ˜ํ™˜
    @GetMapping("/")
    public ModelAndView home() {
        return new ModelAndView("index");
    }

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    @PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto memoRequestDto) {
        return memoService.createMemo(memoRequestDto);
    }

    // Memo ์กฐํšŒํ•˜๊ธฐ
    @GetMapping("/api/memos")
    public List<Memo> getMemos() {
        return memoService.getMemos();
    }
}

2๏ธโƒฃ Service

@Service
@RequiredArgsConstructor // final, @Notnull -> ์ƒ์„ฑ์ž ์ž๋™์ƒ์„ฑ
public class MemoService {

    private final MemoRepository memoRepository;

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    // DB ์ž‘์—…์ฒ˜๋ฆฌ ์ค‘, ์˜ค๋ฅ˜๋ฐœ์ƒ -> ๋ชจ๋“  ์ž‘์—… ์›์ƒํƒœ๋กœ ๋Œ๋ฆผ
    @Transactional
    public Memo createMemo(MemoRequestDto memoRequestDto) {
        // Memo : ๋ฐ˜ํ™˜ํƒ€์ž…(Memo ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฐ˜ํ™˜)
        Memo memo = new Memo(memoRequestDto);
        memoRepository.save(memo);
        return memo;
    }

    // Memo ์กฐํšŒํ•˜๊ธฐ
    @Transactional(readOnly = true)
    public List<Memo> getMemos() {
        // ์ƒ์„ฑ์‹œ๊ฐ„๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ด
        // Repository๋ž‘ ์—ฐ๊ฒฐํ•„์ˆ˜ (๋ฐ˜ํ™˜ํƒ€์ž… + ์กฐํšŒ์กฐ๊ฑด)
        return memoRepository.findAllByOrderByModifiedAtDesc();
    }
}

3๏ธโƒฃ Repository

public interface MemoRepository extends JpaRepository<Memo, Long> {
    // Memo ์กฐํšŒํ•˜๊ธฐ
    List<Memo> findAllByOrderByModifiedAtDesc();

    // ๋ฐ˜ํ™˜ํƒ€์ž… + ์กฐํšŒ์กฐ๊ฑด
}

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


9 Memo์ˆ˜์ • ์„ค๊ณ„

1๏ธโƒฃ Controller

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

    // service๋‹จ ๊ฐ์ฒด ๋ถˆ๋Ÿฌ์˜ด
    private final MemoService memoService;

    // ๋ฉ”์ธํŽ˜์ด์ง€ - html ๋ฐ˜ํ™˜
    @GetMapping("/")
    public ModelAndView home() {
        return new ModelAndView("index");
    }

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    @PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto memoRequestDto) {
        return memoService.createMemo(memoRequestDto);
    }

    // Memo ์กฐํšŒํ•˜๊ธฐ
    @GetMapping("/api/memos")
    public List<Memo> getMemos() {
        return memoService.getMemos();
    }

    // Memo ์ˆ˜์ •ํ•˜๊ธฐ
    @PutMapping("/api/memos/{id}")
    public Long updateMemo(@PathVariable Long id, // RESTful ํ†ต์‹ ๋ฐฉ๋ฒ•
                           @RequestBody MemoRequestDto memoRequestDto) {
        return memoService.updateMemo(id, memoRequestDto);
    }
}

2๏ธโƒฃ Service

@Service
@RequiredArgsConstructor // final, @Notnull -> ์ƒ์„ฑ์ž ์ž๋™์ƒ์„ฑ
public class MemoService {

    private final MemoRepository memoRepository;

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    // DB ์ž‘์—…์ฒ˜๋ฆฌ ์ค‘, ์˜ค๋ฅ˜๋ฐœ์ƒ -> ๋ชจ๋“  ์ž‘์—… ์›์ƒํƒœ๋กœ ๋Œ๋ฆผ
    @Transactional
    public Memo createMemo(MemoRequestDto memoRequestDto) {
        // Memo : ๋ฐ˜ํ™˜ํƒ€์ž…(Memo ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฐ˜ํ™˜)
        Memo memo = new Memo(memoRequestDto);
        memoRepository.save(memo);
        return memo;
    }

    // Memo ์กฐํšŒํ•˜๊ธฐ
    @Transactional(readOnly = true)
    public List<Memo> getMemos() {
        // ์ƒ์„ฑ์‹œ๊ฐ„๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ด
        // Repository๋ž‘ ์—ฐ๊ฒฐํ•„์ˆ˜ (๋ฐ˜ํ™˜ํƒ€์ž… + ์กฐํšŒ์กฐ๊ฑด)
        return memoRepository.findAllByOrderByModifiedAtDesc();
    }

    // Memo ์ˆ˜์ •ํ•˜๊ธฐ
    @Transactional
    public Long updateMemo(Long id, MemoRequestDto memoRequestDto) {
        // ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์˜จ Id -> DB์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ & ๊ฐ์ฒด์— ๋‹ด์•„์˜ด
        // ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์˜จ Id -> DB์— ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("์•„์ด๋””๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
        );

        // Dto๊ฐ’ -> ๊ธฐ์กด ๊ฐ์ฒด(ํŒŒ๋ผ๋ฏธํ„ฐ id๋กœ ์ฐพ์€)์— ๋ฎ์–ด์”Œ์šฐ๊ธฐ
        memo.update(memoRequestDto);

        // Id๊ฐ’ ๋ฐ˜ํ™˜
        return memo.getId();
    }
}

3๏ธโƒฃ Entity

@Getter
@Entity
@NoArgsConstructor // ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€
public class Memo extends Timestamped {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String contents;

    // Memo ๊ฐ’ ๋ฎ์–ด์”Œ์šฐ๋Š” ์—ญํ•  (์ƒ์„ฑ์ž)
    public Memo(MemoRequestDto memoRequestDto) {
        this.username = memoRequestDto.getUsername();
        this.contents = memoRequestDto.getContents();
    }

    // Memo ์ˆ˜์ •ํ•˜๊ธฐ (MemoService -> update)
    public void update(MemoRequestDto memoRequestDto) {
        this.username = memoRequestDto.getUsername();
        this.contents = memoRequestDto.getContents();
    }
}

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

  • ์ดˆ๊ธฐ ์ „์ฒด Memo

  • Memo update

  • Memo ์žฌ์กฐํšŒ


10 Memo์‚ญ์ œ ์„ค๊ณ„

1๏ธโƒฃ Controller

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

    // service๋‹จ ๊ฐ์ฒด ๋ถˆ๋Ÿฌ์˜ด
    private final MemoService memoService;

    // ๋ฉ”์ธํŽ˜์ด์ง€ - html ๋ฐ˜ํ™˜
    @GetMapping("/")
    public ModelAndView home() {
        return new ModelAndView("index");
    }

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    @PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto memoRequestDto) {
        return memoService.createMemo(memoRequestDto);
    }

    // Memo ์กฐํšŒํ•˜๊ธฐ
    @GetMapping("/api/memos")
    public List<Memo> getMemos() {
        return memoService.getMemos();
    }

    // Memo ์ˆ˜์ •ํ•˜๊ธฐ
    @PutMapping("/api/memos/{id}")
    public Long updateMemo(@PathVariable Long id, // RESTful ํ†ต์‹ ๋ฐฉ๋ฒ•
                           @RequestBody MemoRequestDto memoRequestDto) {
        return memoService.updateMemo(id, memoRequestDto);
    }

    // Memo ์‚ญ์ œํ•˜๊ธฐ
    @DeleteMapping("/api/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        return memoService.deleteMemo(id);
    }
}

2๏ธโƒฃ Service

@Service
@RequiredArgsConstructor // final, @Notnull -> ์ƒ์„ฑ์ž ์ž๋™์ƒ์„ฑ
public class MemoService {

    private final MemoRepository memoRepository;

    // Memo ์ƒ์„ฑํ•˜๊ธฐ
    // DB ์ž‘์—…์ฒ˜๋ฆฌ ์ค‘, ์˜ค๋ฅ˜๋ฐœ์ƒ -> ๋ชจ๋“  ์ž‘์—… ์›์ƒํƒœ๋กœ ๋Œ๋ฆผ
    @Transactional
    public Memo createMemo(MemoRequestDto memoRequestDto) {
        // Memo : ๋ฐ˜ํ™˜ํƒ€์ž…(Memo ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฐ˜ํ™˜)
        Memo memo = new Memo(memoRequestDto);
        memoRepository.save(memo);
        return memo;
    }

    // Memo ์กฐํšŒํ•˜๊ธฐ
    @Transactional(readOnly = true)
    public List<Memo> getMemos() {
        // ์ƒ์„ฑ์‹œ๊ฐ„๊ธฐ์ค€ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ด
        // Repository๋ž‘ ์—ฐ๊ฒฐํ•„์ˆ˜ (๋ฐ˜ํ™˜ํƒ€์ž… + ์กฐํšŒ์กฐ๊ฑด)
        return memoRepository.findAllByOrderByModifiedAtDesc();
    }

    // Memo ์ˆ˜์ •ํ•˜๊ธฐ
    @Transactional
    public Long updateMemo(Long id, MemoRequestDto memoRequestDto) {
        // ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์˜จ Id -> DB์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ & ๊ฐ์ฒด์— ๋‹ด์•„์˜ด
        // ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์˜จ Id -> DB์— ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ์ฒ˜๋ฆฌ
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("์•„์ด๋””๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
        );

        // Dto๊ฐ’ -> ๊ธฐ์กด ๊ฐ์ฒด(ํŒŒ๋ผ๋ฏธํ„ฐ id๋กœ ์ฐพ์€)์— ๋ฎ์–ด์”Œ์šฐ๊ธฐ
        memo.update(memoRequestDto);

        // Id๊ฐ’ ๋ฐ˜ํ™˜
        return memo.getId();
    }

    // Memo ์‚ญ์ œํ•˜๊ธฐ
    @Transactional
    public Long deleteMemo(Long id) {
        // Id ์กด์žฌ์—ฌ๋ถ€ ํ™•์ธ -> ์˜ˆ์™ธ์ฒ˜๋ฆฌ
        memoRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("์•„์ด๋””๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
        );

        // Memo ์‚ญ์ œ
        memoRepository.deleteById(id);

        // Id๊ฐ’ ๋ฐ˜ํ™˜
        return id;
    }
}

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

  • Memo ์ƒ์„ฑ ํ›„ ์กฐํšŒ

  • Memo ์‚ญ์ œ

  • Memo ์กฐํšŒ

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

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