본문 바로가기
Back-end/Spring

[Spring] chapter10 애플리케이션 만들기(데이터베이스 조작)

by na1-4an 2023. 8. 3.

아래 글은 스프링 프레임 워크 첫걸음 책을 기반하여 작성한 글입니다.

10-1 애플리케이션 만들기(데이터베이스 조작)

(1) 작성할 내용 확인

No 레이어 컴포턴트 이름 비고
1 애플리케이션 레이어 View - 화면 표시
2 애플리케이션 레이어 Controller Quizcontroller 제어 역할 담당
3 애플리케이션 레이어 Form QuizForm 화면의 게임 폼을 표현
4 도메인 레이어 Service QuizService 인터페이스로 생성
5 도메인 레이어 ServiceImpl QuizServiceImpl Service를 구현
6 도메인 레이어 도메인 객체 Quiz 엔티티 역할
7 도메인 레이어 Repository QuizRepository 인터페이스로 생성
8 인프라스트럭처 레이어 RepositoryImpl - O/R Mapper로 자동 생성
9 인프라스트럭처 레이어 O/R Mapper - 스프링 데이터 JDBC를 사용

위의 표는 chapter09에서 작성한 이 프로젝트에서 필요한 컴포넌트 목록이다.

이번 장에서는 6~9 컴포넌트를 만들어보겠다.

 

(2) application.properties 설정

application.properties 파일을 아래와 같이 수정한다.

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/quizdb
spring.datasource.username=postgres
spring.datasource.password=설정한 비밀번호

10-2 데이터베이스 처리

(1) 도메인 객체 생성

com.example.quiz에 entity 패키지를 만들고 그 아래에 Quiz 클래스를 생성한다.

package com.example.quiz.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Quiz {
    /*식별 아이디*/
    @Id
    private Integer id;
    
    /*퀴즈 내용*/
    private String question;
    /*퀴즈 답*/
    private Boolean answer;
    /*작성자*/
    private String author;
    
}
  • @Data: 모든 필드를 getter와 setter로 접근 가능.
  • @NoArgsConstructor: 기본 생성자를 자동 생성.
  • @AllArgsConstrutor: 모든 필드에 대해 초깃값을 인수로 받는 생성자를 생성.
  • @Id: 테이블의 PK(기본키)인 id와 매핑.

 

(2) Repository 생성

Repository는 인터페이스이다. 데이터베이스의 데이터 정의만! 작성한다.

quiz폴더 안에 repository 패키지 생성후 'QuizRepository' 인터페이스를 생성한다.

 

(3) RepositoryImpl 생성

앞서 만든 인테페이스에 스프링 데이터에서 제공하는 CrudRepository를 상속해서 RepositoryImpl을 만든다.

package com.example.quiz.repository;

import com.example.quiz.entity.Quiz;
import org.springframework.data.repository.CrudRepository;

/** Quiz 테이블: Repository*/
public interface QuizRepository extends CrudRepository<Quiz, Integer>{
    
}

 

파라미터는 엔티티 타입인 Quiz와 @Id 어노텡션을 부여한 필드의 타입인 Integer순으로 지정한다.

  ☞ <저장 대상 객체의 타입, 저장 대상 객체의 기본키>


10-3 데이터베이스 처리 결과 확인

(1) 등록 처리

아래와 같이 QuizApplication 클래스의 코드를 추가한다.

package com.example.quiz;

import com.example.quiz.entity.Quiz;
import com.example.quiz.repository.QuizRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class QuizApplication {
	/** 구동 메서드*/
	public static void main(String[] args) {
		SpringApplication.run(QuizApplication.class, args)
				.getBean(QuizApplication.class).execute();
	}

	/** 주입*/
	@Autowired
	QuizRepository repository;
	
	/** 실행 메서드*/
	private void execute(){
		//등록 처리
		setup();
	}
	
	/** === 퀴즈 2건을 등록합니다 ===*/
	private void setup(){
		//엔티티 생성
		Quiz quiz1 = new Quiz(null, "Spring은 프레임워크 입니까?", true, "Dana");
		//등록 실행
		quiz1 = repository.save(quiz1);
		//등록 확인
		System.out.println("등록한 퀴즈는" + quiz1 + "입니다!");
		
		//엔티티 생성
		Quiz quiz2 = new Quiz(null, "스프링 MVC는 배치 처리를 제공합니까?", false, "Dah");
		//등록 실행
		quiz2 = repository.save(quiz2);
		//등록 확인
		System.out.println("등록한 퀴즈는" + quiz2 + "입니다!");
	}
}
  • @Autowired로 QuizRepository를 주입한다. 

잘 실행이 되었다면 콘솔에는 아래처럼 출력이 되고,

pgAdmin에서는 아래와 같은 결과를 확인할 수 있다.

 

(2) 모든 데이터 취득

아래와 같이 QuizApplication 클래스에 추가한다.

findAll 메서드는 CrudRepository를 상속받아 사용이 가능해지고, 이는 엔티티 전체를 취득할 수 있게 한다.

	/** === 모든 데이터 취득 === */
	private void showList(){
		System.out.println("--- 모든 데이터 취득 개시 ---");
		// 리포지토리를 이용해 모든 데이터를 취득해서 결과를 반환
		Iterable<Quiz> quizzes = repository.findAll();
		for (Quiz quiz : quizzes){
			System.out.println(quiz);
		}
		System.out.println("--- 모든 데이터 취득 완료 ---");
	}

execute() 메서드에 setup을 주석 처리하고 showList()를 추가해주면 아래와 같이 결과를 확인할 수 있다.

나는 깜빡하고 setup()을 주석처리를 안해서 저렇게 되었다..ㅎㅎ

** 참고

Iterable???: 반복 가능한 객체를 뜻한다. (Iterable object)

 

(3) 한 건 데이터 취득

래와 같이 QuizApplication 클래스에 추가한다.

findById 메서드는 CrudRepository를 상속받아 사용이 가능해지고, 해당 id에 맞는 1가지 엔티티를 취득할 수 있게 한다.

ispresent 메서드로 값이 있는지 확인하고, get 메서드로 래핑한 값을 취득한다.

	/** === 한 건의 데이터 취득 === */
	private void showOne(){
		System.out.println("--- 1건 취득 게시 ---");
		//리포지토리를 이용해 1건의 데이터를 취득해서 결과를 반환(반환값은 Optional)
		Optional<Quiz> quizOpt = repository.findById(1);
		//반환값이 있는 지 확인
		if (quizOpt.isPresent()){
			System.out.println(quizOpt.get());
		}
		else {
			System.out.println("해당 데이터는 존재하지 않습니다.");
		}
		System.out.println("--- 1건 취득 완료 ---");
	}

마찬가지로 다른 함수들은 주석 처리하고 showOne()만 실행시켜 보겠다.

** 참고

Optional<T>???: NULL이 올 수 있는 값을 감싸는 Wrapper 클래스로, NPE(NullPointException)가 발생하지 않도록 도와준다.

 

(4) 변경 처리

아래와 같이 QuizApplication 클래스에 추가한다.

save메서드는 CrudRepository를 상속받아 사용이 가능해지고, 이는 아래와 같은 기능을 한다.

  • @Id 어노테이션이 부여된 필드가 null인 경우에는 INSERT 문을 실행하고,
  • null이 아닌 경우에는 UPDATE문을 실행한다.
	/** === 변경 처리 === */
	private void updateQuiz(){
		System.out.println("--- 변경 처리 개시---");
		//변경할 엔티티를 생성
		Quiz quiz1 = new Quiz(1, "스프링은 프레임 워크입니까? ", true, "변경 담당");
		//변경 처리
		quiz1 = repository.save(quiz1);
		//변경 결과 확인
		System.out.println("변경된 데이터는 " + quiz1 + "입니다.");
		System.out.println("--- 변경 처리 완료 ---");
	}

마찬가지로 다른 함수들은 주석 처리하고 updateQuiz()만 실행시켜 보겠다.

 

(5) 삭제 처리

아래와 같이 QuizApplication 클래스에 추가한다.

	private void deleteQuiz(){
		System.out.println("--- 삭제 처리 개시 ---");
		// 삭제 실행
		repository.deleteById(4);
		System.out.println("--- 삭제 처리 완료 ---");
	}

삭제가 잘 되었는지 확인하기 위해 showList() 함수를 실행시켜 보겠다.