Practice/Java

스프링 코드 분석) 메모장 만들기 프로그램

밍미a 2022. 12. 8. 02:01
728x90

오늘 분석할 코드

 

controller 패키지

더보기

MemoController.java

package com.example.hanghaememo.controller;


import com.example.hanghaememo.dto.MemoRequestDto;
import com.example.hanghaememo.entity.Memo;
import com.example.hanghaememo.service.MemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@RestController
@RequiredArgsConstructor
public class MemoController {

    private final MemoService memoService;
    @GetMapping("/")
    public ModelAndView home() {
        return new ModelAndView("index");
    }

    @PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto requestDto) {
        return memoService.createMemo(requestDto);
    }

    @GetMapping("/api/memos")
    public List<Memo> getMemos() {
        return memoService.getMemos();
    }

    @PutMapping("/api/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        return memoService.update(id, requestDto);
    }

    @DeleteMapping("/api/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        return memoService.deleteMemo(id);
    }
}

 

dto 패키지

더보기

MemoRequestDto.java

package com.example.hanghaememo.dto;

import lombok.Getter;

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

 

entity 패키지

더보기

Memo.java

package com.example.hanghaememo.entity;

import com.example.hanghaememo.dto.MemoRequestDto;
import com.example.hanghaememo.repository.MemoRepository;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@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;

    public Memo(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }

    public void update(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
}
더보기

Timestamped.java

package com.example.hanghaememo.entity;

import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Timestamped {

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime modifiedAt;
}

repository 패키지

더보기

MemoRepository.java (인터페이스)

package com.example.hanghaememo.repository;

import com.example.hanghaememo.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface MemoRepository extends JpaRepository<Memo, Long> {
    List<Memo> findAllByOrderByModifiedAtDesc();
}

service 패키지

더보기

MemoService.java

package com.example.hanghaememo.service;

import com.example.hanghaememo.dto.MemoRequestDto;
import com.example.hanghaememo.entity.Memo;
import com.example.hanghaememo.repository.MemoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.transaction.*;
import java.util.List;

@Service
@RequiredArgsConstructor
public class MemoService {
    private final MemoRepository memoRepository;

    @Transactional
    public Memo createMemo(MemoRequestDto requestDto) {
        Memo memo = new Memo(requestDto);
        memoRepository.save(memo);
        return memo;
    }

    @Transactional(readOnly = true)
    public List<Memo> getMemos() {
        return memoRepository.findAllByOrderByModifiedAtDesc();
    }

    @Transactional
    public Long update(Long id, MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id).orElseThrow(
                ()-> new IllegalArgumentException("아이디가 존재하지 않습니다.")
        );
        memo.update(requestDto);
        return memo.getId();
    }

    @Transactional
    public Long deleteMemo(Long id) {
        memoRepository.deleteById(id);
        return id;
    }
}

1. 파일구조 톺아보기

Q. 왜 패키지를 controller, dto, entity, repository, service 로 나눈거지? 얘네의 역할이 뭔데..?

A. controller, dto, entity, repository, service 의 역할:

  • controller :
    • 사용자의 요청이 진입하는 지점이며 요청에 따라 어떤 처리를 할지 결정을 Service에 넘겨준다. 그후 Service에서 실질적으로 처리한 내용을 View에게 다시 넘겨주는 역할을 한다.
  • dto :
    • DTO(Data Transfer Object) 는 계층 간 데이터 교환을 하기 위해 사용하는 객체로 JSON serialization과 같은 직렬화에도 사용되는 객체이다. DTO는 getter, setter 메서드를 포함하며, 이 외의 비즈니스 로직을 가지지 않는 순수한 데이터 객체이다. 유저가 입력한 데이터를 DB에 넣는 과정에서 이 DTO에 넣어서 데이터를 전송하게 된다.
  • entity :
    • Entity는 실제 DB 테이블과 매핑되는 핵심 클래스이다. 이를 기준으로 테이블이 생성되고 스키마가 변경된다. 따라서, 절대로 Entity를 요청이나 응답값을 전달하는 클래스로 사용해서는 안 된다.
      Entity는 id로 구분된다. 그리고 비즈니스 로직을 포함할 수 있다.
  • repository :
    • Entity에 의해 생성된 DB에 접근하는 메서드 들을 사용하기 위한 인터페이스이다. @Entity라는 어노테이션으로 데이터베이스 구조를 만들었다면 이것을 어떻게 할 것인지 정의해주는 역할을 한다.
  • service :
    • 백엔드에서 처리해야 할 비지니스 로직들을 sevice가 수행한다. 
    • -service를 이해하기 위한 과정 -
        1. Client가 Request를 보낸다.(Ajax, Axios, fetch등..)
        2. Request URL에 알맞은 Controller가 수신 받는다. (@Controller , @RestController)
        3. Controller 는 넘어온 요청을 처리하기 위해 Service 를 호출한다.
        4. Service는 알맞은 정보를 가공하여 Controller에게 데이터를 넘긴다.
        5. Controller 는 Service 의 결과물을 Client 에게 전달해준다.
https://melonicedlatte.com/2021/07/24/231500.html

https://velog.io/@jybin96/Controller-Service-Repository-%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

https://wildeveloperetrain.tistory.com/101

https://tecoble.techcourse.co.kr/post/2021-05-16-dto-vs-vo-vs-entity/

2. 데이터의 이동 톺아보기

3. 어노테이션의 의미 정리하기 (내일 아침에 마저 정리.)

[package controller]
[class MemoController]

  • @RestController :
    • 컨트롤러 클래스 하위 메서드에 @ResponseBody 어노테이션을 붙이지 않아도 문자열과 JSON 등을 전송할 수 있다. 
https://doctorson0309.tistory.com/664
  • @RequiredArgsConstructor :
    • 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 준다. 주로 의존성 주입 편의성을 위해 사용된다. 그러나 . setter 메서드가 필요없는 필드에 대해서도 setter 메서드를 강제로 생성하게 되니, 필드 값이 변경될 위험이 생기게 될 수 있다. 따라서 너무 무분별하게 사용하는것은 좋지 않다.
https://medium.com/webeveloper/requiredargsconstructor-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-dependency-injection-4f1b0ac33561
  • @GetMapping("/"):
    • HTTP GET 요청이 들어오면 특정 메서드에 매핑해주기 위한 어노테이션이다. 
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/GetMapping.html

ModelAndView   클래스의 용도는 뭐지...?

  • @PostMapping("/api/memos"):
    • HTTP POST요청이 들어오면 특정 메서드에 매핑해주기 위한 어노테이션이다.

※ @RequestMapping 대신에 @GetMapping 이나 @PostMapping을 쓰는 이유 :

  • 코드가 줄어든다.
  • url을 중복 사용할 수 있다. /api/memos url을 사용한다고 했을 때, @PostMapping("/api/memos") 이나 @GetMapping("/api/memos") 이렇게 2번 쓸 수 있는데 @RequestMapping 는 한번밖에 못쓰니까..!
  • 어떤 타입으로 들어오는지 명시된다. 코드만 봐도 어떤 전송 방식을 처리하는지 쉽게 확인 할 수 있다.
  • GET의 경우는 통상적으로 데이터를 화면에 뿌릴 때 많이 사용되고, POST는 전송한 데이터를 insert 할 때 많이 사용된다. 
https://change-words.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-RequestMapping-%EB%8C%80%EC%8B%A0-PostMapping-GetMapping-%EC%93%B0%EB%8A%94-%EC%9D%B4%EC%9C%A0
  • @PutMapping("/api/memos/{id}"):
    • PutMapping은 데이터를 update할 때 사용하는 매핑이다.

※ PUT과 POST의 차이는 멱등성으로 설명되어있다. 멱등성이란, 여러번 연속해서 호출해도 클라이언트가 받는 응답은 동일하다는 것이다. PUT은 멱등성이 존재하고, POST는 멱등성이 존재하지 않는다.

https://javacoding.tistory.com/142

Q. 멱등성을 이해하기 쉬울만한 예시는 뭐가 있을까...?

https://unknownpgr.com/posts/idempotent/
  • @RequestBody
    • 스프링에서 비동기 처리를 하는 경우 @RequestBody , @ResponseBody를 사용한다.
    • 이 어노테이션이 붙은 파라미터에는 http요청의 본문(body)이 그대로 전달된다.
    • xml이나 json기반의 메시지를 사용하는 요청의 경우에 이 방법이 매우 유용하다. 왜냐하면 HTTP 요청의 바디내용을 통째로 자바객체로 변환해서 매핑된 메소드 파라미터로 전달주기 때문이다.
https://cheershennah.tistory.com/179
  • @PathVariable
    • URL 경로에 변수를 넣어주는 어노테이션.
    • 예를들면 https://mingmi1234567890.tistory.com/88 에서 88 같이 표현해준다. 
https://byul91oh.tistory.com/435
  • @DeleteMapping("/api/memos/{id}")
    • @DeleteMapping 은 데이터를 delete 할 때 사용하는 매핑이다.
https://ksyy.tistory.com/255
 

[package dto]
[class MemoRequestDto ]

  • @Getter
    • 자동으로 게터 접근자를 생성해준다.
https://www.daleseo.com/lombok-popular-annotations/

[package entity]
[class Memo]

  • @Getter
    • 자동으로 게터 접근자를 생성해준다.
  • @Entity
    • @Entity가 붙은 클래스는 JPA가 ㅘㄴ리해주며, JPA를 사용해서 DB 테이블과 매핑할 클래스는 @Entity를 꼭 붙여야만 매핑이 가능하다.
    • @Entity 사용할 시 주의사항..!
      • 접근 제어자가 public 혹은 protected  기본 생성자가 필수이다..
      • 구현체에 따라 되는 것도 있으나, 스펙상 사용하지 못하도록 해두었기 때문에 사용하지 않는 것이 좋다.
      • final 클래스, enum, interface, inner 클래스에는 사용이 불가능하다
      • 저장하려는 속성은 final이면 안된다.
 https://ttl-blog.tistory.com/112
  • @NoArgsConstructor
    • 파라미터가 없는 기본 생성자를 생성해준다.
https://www.daleseo.com/lombok-popular-annotations/
  • @Id
    • Id 임을 정의해주는 어노테이션이다. @Id가 붙으면 PK값으로 지정된다.
  • @GeneratedValue(strategy = GenerationType.AUTO)
    • Id값을 만들어 주기 위해 사용되며 GenerationType.AUTO 이면 자동으로 Id값을 생성해줍니다. 
  • @Column(nullable = false)
    • 칼럼의 NOT NULL 제약조건을 걸어줄 때 사용한다. 

[class Timestamped]

  • @Getter
    • 자동으로 게터 접근자를 생성해준다.
  • @MappedSuperclass
    • 부모 클래스를 상속받는 자식 클래스에게 매핑 정보만 제공하고 싶을 때 이 어노테이션을 사용한다. 엔티티는 엔티티만 상속받을 수 있기 때문에 엔티티가 아닌 클래스를 상속받기 위해서 @MappedSuperclass 를 사용한다.
https://feco.tistory.com/13
  • @EntityListeners(AuditingEntityListener.class)
  • @CreatedDate
  • @LastModifiedDate

?? 타임스탬프가 안쓰이는것 같은데 왜 넣었지...?

[package service]
[class MemoService]

  • @Service
  • @RequiredArgsConstructor
  • @Transactional
  • @Transactional(readOnly = true)

 

[class HanghaememoApplication]

  • @EnableJpaAuditing
  • @SpringBootApplication

 

4. 내 현재 위치 파악하고 모르는것들 리스트업 하기.

 

  • 어노테이션의 의미와 활용방법을 잘 모른다.
    • 해결방법 : 일단 어노테이션들 하나하나 찾아보면서 개념 정리.
    • 어노테이션 공부할때 어떨 때 써야하고 어떨 때 쓰면 안되는지 장단점 구분해서 공부하면 좋을 것 같다.
  • 객체지향적으로 클래스 분류하는게 아직 어렵다.
    • 클래스를 나눌 때 왜 거기 들어가야하는지 근거를 적으면서 분류를 하면 좀 나으려나..?
  • 필요한 메서드가 어느 클래스 안에 들어가 있어야 하는지 어렵다. >> 객체지향 분류랑 비슷한 문제사항
  • 예외처리는 어떻게 해야하는지 모르겠다.

 


더 공부할 내용

궁금1. 파일구조 뜻 검색하다보니 다들 VO 랑 DTO랑 Entity를 비교하던데... VO 는 뭐지..? 나중에 좀 더 알아보자 !

어노테이션들의 활용방법에 대해 더 자세히 알아보자..!

REST API 가 뭐지..!?

의존성 주입이 뭘까..? 

 

 

 


느낀점

모르는게 너무 많다.. 궁금한것도 많은데.. 인터넷에 올라온 글들 만으로는 이해하기가 조금 어렵다

책이라도 사서 봐야하나...? 시간이 부족한데...ㅠㅠ

늦은시간까지 공부를 해도 계속 뒤쳐지고 있는 기분이 든다

 


참고 블로그

https://appleg1226.tistory.com/30

 

찐 개발자로의 성장을 위한 코드 분석 시작해보기(feat. Spring Framework)

계기와 결심 라이브러리나 프레임워크를 사용하는 것은 개발자에겐 일상이다. 시간이 지날수록 프레임워크들은 편리하게 발전하고 있으며, 그 때문에 우리는 비즈니스 로직을 처리하는 것에만

appleg1226.tistory.com