Sparta에서 2주차까지 수업을 듣고나니 여태까지 배운 것을 스스로 해보는 숙제가 주어졌다.
숙제를 진행하면서 기억해놓고 싶은 것들✨을 적어보았다.
0. 프로젝트 Setting
🔥 Spring boot 프로젝트 Setting을 진행한다.
<참고>
SpringBoot페이지에서 아래와 같이 프로젝트 셋팅을 진행하였다. 다운로드받은 후, 알집을 풀어준다.
그후, IDE(본인은 IntelliJ)에서 새 프로젝트 생성 버튼을 클릭하여, (Gradle Project인 경우) build.gradle 파일 선택 후 open as project로 열면 프로젝트가 잘 셋팅된다.
1. 디렉토리 구조
🔥 필요한 폴더는 총 Domain, Service, Controller 이다.
📁 domain
└ Person 클래스
└ Person 레포지토리
└ Timestamped 클래스
└ PersonRequestDto 클래스
📁 service
└ PersonService 클래스
📁 controller
└ PersonController 클래스
2. Person 클래스 (@Entity
)
🔥 Person 클래스에는 3개의 멤버변수를 두었다.
(1) 사람의 이름, 주소, 직업을 멤버변수로 두었고, 각자 name, address, job 으로 하였다.
(2) 멤버변수 설정
- id는 자동으로 생성되도록 annotation하였고 나머지 3개는 not null로 설정하였다.
- 기본생성자와 매개변수 있는 생성자는 어노테이션으로 추가하였다.
- getter 또한 어노테이션으로 추가한다.
[domain/Person.java 코드]
@Getter
@NoArgsConstructor
@Entity
public class Person extends Timestamped{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String address;
@Column(nullable = false)
private String job;
// PersonController에서 Person(requestDto)로 새 데이터 생성함
// API 응답 시 사용됨
public Person(PersonRequestDto requestDto) {
this.name = requestDto.getName();
this.address = requestDto.getAddress();
this.job = requestDto.getJob();
}
//매개변수있는 생성자.
// main 함수에서 사용함!
public Person(String name, String job, String address){
this.name = name;
this.address = address;
this.job = job;
}
//personRequestDto로 업데이트 시
public void update(PersonRequestDto requestDto) {
this.name = requestDto.getName();
this.address = requestDto.getAddress();
this.job = requestDto.getJob();
}
}
3. PersonRepository 클래스
🔥 데이터를 관리하는 클래스로, JPARepository 인터페이스를 상속받는다.
- JPARepository 인터페이스를 상속하여 findAll(), findById(), save(), delete() 등등 을 사용할 수 있다.
[domain/PersonRepository.java 코드]
public interface PersonRepository extends JpaRepository<Person, Long> {
}
4. Timestamped 클래스
🔥 데이터를 저장, 수정 시 자동으로 날짜 및 시간이 저장되도록 한다.
- 추상클래스이기 때문에 상속받아서 사용할 수 있다.
- 데이터타입이 LocalDateTime인 createdAt과 modifiedAt 멤버변수 생성한다.
- main 함수를 갖고 있는 클래스(FirstprojectApplication)에 @EnableJPAAuditing을 추가하여 JPA를 활성화한다. (그래야 데이터 추가, 수정 시 반영된다.)
- JPA Entity 클래스들(Person)이 해당 추상 클래스를 상속(extends)할 경우 createdAt과 modifiedAt를 컬럼으로 인식하도록 @MapperSuperclass 추가
- Person 클래스에서 상속! ( extends Timestamped )
[domain/Timestamped.java 코드]
@MappedSuperclass //JPA Entity 클래스들(Person)이 해당 추상 클래스를 상속(extends)할 경우 createDate, modifiedDate를 컬럼으로 인식
@EntityListeners(AuditingEntityListener.class) //해당 클래스에 Auditing 기능을 포함
public abstract class Timestamped {
@CreatedDate //Entity가 생성되어 저장될 때 시간이 자동 저장
private LocalDateTime createdAt;
@LastModifiedDate //조회한 Entity의 값을 변경할 때 시간이 자동 저장
private LocalDateTime modifiedAt;
}
5.FirstprojectApplication 클래스 (main
메서드 포함)
🔥 테스트를 위해 @Bean을 사용해서 commandLine으로 확인해본다.
- PersonRepository 이용해서 데이터 추가, 전체 조회, id로 조회, 데이터 삭제 진행
- 데이터 수정은 Service의 역할!
- But❗ 데이터를 업데이트하기 위해 DB와 맞닿아 있는 Person 클래스로 생성하는 것이 과연 안전할까?
- 완충재로 활용하는 것이 DTO(Data Transfer Object) -> PersonRequestDto 클래스 생성
- But❗ 데이터를 업데이트하기 위해 DB와 맞닿아 있는 Person 클래스로 생성하는 것이 과연 안전할까?
- 서버를 run하면 h2-console에서 저장된 데이터를 확인할 수 있다.
- 터미널의 commandLine으로도 확인 가능하다.
[src/main/java/com/moonz/firstproject/FirstprojectApplication.java 코드]
/* Controller 역할과 달리, 당장 JVM 실행 시, main에서 데이터 관련 테스트 해보는 것 */
@EnableJpaAuditing // JPA Auditing을 활성화하여 수정&생성 일자가 spring에 반영될 수 있도록 함
@SpringBootApplication
public class FirstprojectApplication {
public static void main(String[] args) {
SpringApplication.run(FirstprojectApplication.class, args);
}
@Bean
public CommandLineRunner demo(PersonRepository personRepository, PersonService personService) {
return (args) -> {
//==================================== 데이터 추가 ==========================================
Person person = new Person("Moon", "Student", "Hwaseongsi");
personRepository.save(person);
//==================================== 데이터 조회 ==========================================
System.out.println("=======전체 데이터 조회=======");
List<Person> personList = personRepository.findAll();
for (int i=0; i<personList.size(); i++) {
Person p = personList.get(i);
System.out.println("ID: "+ p.getId());
System.out.println("이름: "+ p.getName());
System.out.println("직업: "+ p.getJob());
System.out.println("주소: "+ p.getAddress());
}
//==================================== 데이터 업데이트 ==========================================
PersonRequestDto requestDto = new PersonRequestDto("Moonz", "Influencer", "Suwonsi");
personService.update(1L, requestDto); //위에서 생성한 id=1인 데이터를 수정
System.out.println("=======데이터 업데이트 완료=======");
//==================================== 데이터 1개 조회 ==========================================
System.out.println("=======데이터 1개 조회=======");
Person one = personRepository.findById(1L).orElseThrow(
() -> new NullPointerException("아이디가 존재하지 않습니다.") //객체가 생성되지 않은 것
);
System.out.println("ID: "+ one.getId());
System.out.println("이름: "+ one.getName());
System.out.println("직업: "+ one.getJob());
System.out.println("주소: "+ one.getAddress());
};
}
}
6. PersonRequestDto 클래스
🔥 데이터를 수정하기 전, Person 객체를 임시 생성하는 역할
- 똑같이 멤버변수 3개 생성
- 기본생성자 생성, 매개변수있는 생성자 생성, getter 생성은 어노테이션으로 추가
[domain/PersonRequestDto.java 코드]
@NoArgsConstructor
@Getter
public class PersonRequestDto {
private String name;
private String job;
private String address;
//생성자
public PersonRequestDto(String name, String job, String address) {
this.name = name;
this.address = address;
this.job = job;
}
}
7. PersonService
🔥 데이터 수정 시에는 PersonRepository가 아닌 Service에서 진행한다.
- update 메서드는 update될 객체 id와 반영될 Person 정보가 인자로 필요하다.
- 위에서 언급했듯이, 반영할 객체를 위해 Person 클래스로 접근해서 생성하는 것은 안전하지 않으므로, PersonRequestDto 클래스를 사용한다.
- JPARepository로 해당 id의 Person 객체 정보를 조회하여
- RequestDto로 해당 Person 객체를 Update 시킨다. (이때는 Person 클래스에 접근해야함. 이를 위해 Person 클래스에도 update 메서드를 정의해준다.)
[service/PersonService.java 코드]
@RequiredArgsConstructor
@Service
public class PersonService {
private final PersonRepository personRepository;
//personRepository의 생성자를 lombok @RequiredArgsConstructor 가 대신 해줌
// public PersonService(PersonRepository personRepository) {
// this.personRepository = personRepository;
// }
@Transactional
public Long update(Long id, PersonRequestDto requestDto){
Person person1 = personRepository.findById(id).orElseThrow(
()-> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
person1.update(requestDto);
return person1.getId();
}
}
8. PersonController
🔥 CommandLine으로 확인해봤으니, 이제 Rest API 요청에 대한 응답 역할을 할 Controller를 생성한다.
- 5번에서 작성한 코드를 각 REST API (데이터 조회 GET, 추가 POST, 수정 PUT, 삭제 DELETE) 에 이용할 수 있다.
- @XXXMapping("url") : 각 메서드마다 해당 어노테이션을 붙여주면, api 호출 시 자동으로 메서드가 매핑되어 실행한다.
EX) @PutMapping("/api/persons/{id}") : 해당 url로 PUT요청을 하면 해당 어노테이션이 붙은 메서드가 호출된다. - @RequestBody : POST 요청 시 body에 담아 전송된 데이터를 인자에 담아주기 위한 annotation
@PathVariable : PUT, DELETE 요청 시 url에 담아 전송된 데이터를 인자에 담아주기 위한 annotation
[controller/PersonController.java 코드]
@RequiredArgsConstructor
@RestController
public class PersonController {
private final PersonRepository personRepository;
private final PersonService personService;
/* Controller는 API 요청 시 응답을 처리하는 역할!
데이터를 조회, 추가, 삭제 시에는 PersonRepository를 이용(findById(), save(), deleteById())
데이터를 수정 시에는 PersonService를 이용(update())
PersonRequestDto를 이용해서 데이터 추가, 수정 진행!
데이터 수정, 삭제 시에는 id 이용!
*/
//추가
@PostMapping("/api/persons")
public Person creatPerson(@RequestBody PersonRequestDto requestDto) {
Person person = new Person(requestDto);
return personRepository.save(person);
}
//조회
@GetMapping("/api/persons")
public List<Person> getPersons() {
return personRepository.findAll();
}
//수정
@PutMapping("/api/persons/{id}")
public Long updatePerson(@PathVariable Long id, @RequestBody PersonRequestDto requestDto) {
return personService.update(id,requestDto);
}
//삭제
@DeleteMapping("/api/persons/{id}")
public Long deletePerson(@PathVariable Long id) {
personRepository.deleteById(id);
return id;
}
}
👉 Postman을 이용하여 API 호출 테스트 완료한다!
전체코드는 여기를 참조하세요 🥰
'백엔드 개발하며 작성한 > Spring' 카테고리의 다른 글
JAP Query로 특정 칼럼의 count 쿼리문 실행하기 (0) | 2021.11.12 |
---|---|
[Spring Boot 프로젝트] AWS EC2로 Spring Boot 배포 (0) | 2021.10.16 |
[Spring Boot] 테스트 코드 작성 (Hello World편) (0) | 2021.10.13 |
[Spring Boot 프로젝트] 1. 프로젝트 생성과 Boot Strap 템플릿 적용(Thymeleaf) (0) | 2021.10.08 |
ORM(Object Relational Mapping)과 JPA(Java Persistence API) (0) | 2021.10.07 |