스프링(Spring)
스프링은 자바를 이용한 클라이언트와 DB의 연결을 수행할 수 있도록 돕는 프레임워크다. 동적인 웹 개발에 많이 쓰인다.
스프링의 요소
엔티티(Entity)
- DB에서의 테이블과 같은 역할. JPA를 통해 실제 DB와의 상호작용을 한다.
// @Entity 선언시 JPA를 사용할 때 RDBMS의 테이블과 같은 역할을 하게 된다.
@Entity
public class Person{
// @Id : 기본키로 지정
// @GeneratedValue(stratedgy = GenerationType.AUTO) : Auto_increment 설정
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// @Column(nullable = false) : 테이블의 필드로 지정. 뒤의 옵션은 not null.
@Column(nullable = false)
private String name;
}
JPA(Java Persistence API)?
- 자바 내에서 RDBMS를 사용하는 방식을 정의해놓은 인터페이스이다.
- SQL을 직접 작성해 DB에 접근하던 기존 방식과는 달리, 미리 선언해놓은 Entity와 DB의 테이블을 매핑함으로써 자바의 메소드 형식으로 DB에 접근할 수 있게 해준다.
- 큰 장점 중 하나는, RDBMS마다 존재하는 미세한 문법의 차이를 고려할 필요 없이 명령어를 호출해 사용할 수 있다.
컨트롤러(Controller)
- 클라이언트의 요청(request)에 대한 응답(response)를 수행하는 패키지로, 요청에 따라 어떤 처리를 할지 결정하는 역할을 한다.
// @RestController : response 값을 JSON 형태로 반환해줌. 추가로 Repository와 Service를 이용할 수 있는 상태로 만들어준다.
// @RequiredArgsConstructor : @NonNull, final 필드에 대한 생성자를 자동으로 만들어줌.
// repository와 service는 생성자로 만들어놓기만 하면 스프링이 알아서 불러온다.
@RequiredArgsConstructor
@RestController
public class PersonController {
private final PersonRepository personRepository;
private final PersonService personService;
// @GetMapping("/url") : GET 방식의 처리. 조회 기능
@GetMapping("/api/person")
public List<Person> getPerson(){
return personRepository.findAll();
}
// @PostMapping("/url") : POST 방식의 처리. 삽입 수행
// POST 방식으로 받아온 데이터는 @RequestBody로 선언된 매개변수에 매핑된다.
// @RequestBody로 받아오기 위해서는 request를 할 때 반드시
// Content-Type : application/json 선언을 해줘야 한다.
@PostMapping("/api/person")
public Person postPerson(@RequestBody PersonRequestDto requestDto){
Person person = new Person(requestDto);
return personRepository.save(person);
}
// @PutMapping("/url/{id}") : PUT 방식의 처리. 수정 수행
// URL로 받아온 데이터는 @PathVariable로 선언된 매개변수에 매핑된다.
@PutMapping("/api/person/{id}")
public Long putPerson(@PathVariable Long id, @RequestBody PersonRequestDto requestDto){
return personService.update(id, requestDto);
}
// @DeleteMapping("/url/{id}") : DELETE 방식의 처리. 삭제 수행
@DeleteMapping("/api/person/{id}")
public Long deletePerson(@PathVariable Long id){
personRepository.deleteById(id);
return id;
}
}
리포지토리(Repository)
- 엔티티를 조작하기 위한 인터페이스로, DB에 CRUD 명령을 실행하게 만드는 인터페이스이다.
// 선언만 해놓고 Controller에서 인스턴스를 생성해 사용한다.
public interface PersonRepository extends JpaRepository<Person, Long> {
}
엔티티를 불러오기 위해서는 JpaRepository<엔티티의 클래스, 기본키의 타입>를 상속받아야 한다.
리포지토리 주요 기능은 컨트롤러 설명에 적혀있다. 메소드명으로 쉽게 때려맞출 수 있다.
서비스(Service)
- 컨트롤러와 리포지토리 사이를 연결해준다.
- 테이블의 데이터(엔티티)를 직접적으로 선언해 관리해주는 유일한 클래스. 다른 곳은 DTO를 통한 간접적인 통신만 시행한다.
- 대표적으로는 레코드 수정(put, update) 기능을 수행.
- 컨트롤러가 지정하는 처리 중 대부분을 수행하는 패키지로, 요청을 받아 수행하게 될 로직들을 하나의 서비스 단위로 묶은 트랜잭션을 생성하여 처리한다.
@RequiredArgsConstructor
// @Service : 서비스임을 선언해줘야 스프링이 서비스로 취급해준다.
@Service
public class PersonService {
private final PersonRepository personRepository;
// @Transactional : 해당 메소드 안에서 작동하는 엔티티의 변화는 실제 DB에 반영될 것임을
// 선언하는 구문.
@Transactional
public Long update(Long id, PersonRequestDto requestDto){
Person person = personRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("존재하지 않는 데이터입니다.")
);
person.update(requestDto);
return person.getId();
}
}
DTO(Data Transfer Object)?
- 직접적인 엔티티 인스턴스의 생성 및 접근을 막기 위해 Service를 제외한 공간에서는 DTO라는 엔티티의 속성값만을 가지는 클래스를 이용해 JPA 작업을 수행한다.
- JPA를 이용할때뿐만 아니라, ORM 운용이나, 외부 솔루션과 연결해 데이터를 통신할때도 DTO를 이용한다.
롬복(Lombok)
롬복은 어노테이션 기반의 라이브로리로, 빈번하게 사용되는 코드들을 간소화할 수 있도록 돕는 역할을 한다.
사실 롬복이 수행하는 많은 기능들은 기존 IDE에서 자동완성을 제공하는 기능(getter/setter, 생성자 등)들이라 반드시 필요하다고 볼 순 없지만, 사용하는 사람들이 많은 라이브러리이므로 알아두는것이 좋을 것이다.
@Getter
@Setter
@RequiredArgsConstructor
public class PersonRequestDto {
private final String name;
private final int age;
}
위에 어노테이션 세 줄 붙인 것으로 getter, setter, final 또는 @NonNull 선언이 되어있는 필드에 대한 생성자 정의가 끝났다. 매우 간편하다. 이 외에도 빈 생성자를 생성해주는 @NoArgsConstructor, 모든 필드가 포함되어있는 생성자를 생성해주는 @AllArgsConstructor 외에도 많은 편의성 기능들을 제공해준다.
필드의 변화가 많을때 별도의 품을 들이지 않고 사용할 수 있다는 점에서 편리하며, 확실히 코드가 깔끔하다. 하지만 기존의 방식에서 큰 불편함을 느끼지 않는 사람이라면 그다지 애용할 것 같은 기능은 아니다.
단점 또한 명확하다.
일부 필드의 Getter/Setter 기능을 제한하려면 해당 필드에 접근해 특별히 @Getter(AccessLevel.NONE) 선언을 해줘야하기 때문에 어떻게 보면 오히려 번거로울수도 있다.
추가로, 일반적인 Setter 방식이 아닌 경우(Setter로 받아와 값을 필터링해서 담아주는 경우)에도 같은 방식으로 선언해준 뒤 작업을 수행해줘야하기 때문에 만약 Setter 메소드의 커스텀을 요구하는 필드가 많을 경우에는 오히려 어노테이션을 통한 선언이 클래스의 직관적이지 못 한 부분을 만들어낼 수 있다. 잘 구분해서 사용하자. 아니면 롬복을 사용하기 좋은 구조로 설계를 하든가.
'기술 > Spring-Boot' 카테고리의 다른 글
Controller는 뭘 하는 녀석일까? (0) | 2021.11.26 |
---|---|
@RequestBody는 왜 Setter가 없어도 작동할까? (0) | 2021.11.24 |
항해99 11/19(금) 스프링 TIL (0) | 2021.11.19 |
항해99 11/18(목) 스프링 TIL (0) | 2021.11.18 |
항해99 11/16(화) 스프링 TIL (0) | 2021.11.17 |
댓글