백엔드 개발하며 작성한/Spring

[Spring Boot] 프로젝트 세팅부터 REST API까지

뭉지(moonz) 2021. 9. 26. 20:41
반응형

Sparta에서 2주차까지 수업을 듣고나니 여태까지 배운 것을 스스로 해보는 숙제가 주어졌다.

숙제를 진행하면서 기억해놓고 싶은 것들✨을 적어보았다.


0. 프로젝트 Setting

🔥 Spring boot 프로젝트 Setting을 진행한다.

<참고>

SpringBoot페이지에서 아래와 같이 프로젝트 셋팅을 진행하였다. 다운로드받은 후, 알집을 풀어준다.

그후, IDE(본인은 IntelliJ)에서 새 프로젝트 생성 버튼을 클릭하여, (Gradle Project인 경우) build.gradle 파일 선택 후 open as project로 열면 프로젝트가 잘 셋팅된다.

text

 

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인 createdAtmodifiedAt 멤버변수 생성한다.
  • main 함수를 갖고 있는 클래스(FirstprojectApplication)에 @EnableJPAAuditing을 추가하여 JPA를 활성화한다. (그래야 데이터 추가, 수정 시 반영된다.)
  • JPA Entity 클래스들(Person)이 해당 추상 클래스를 상속(extends)할 경우 createdAtmodifiedAt를 컬럼으로 인식하도록 @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 클래스 생성
  • 서버를 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 호출 테스트 완료한다!


전체코드는 여기를 참조하세요 🥰

반응형