이 글은 GDSC Ewha 서버 파트의 2024 솔루션 챌린지를 위해 진행한 Springboot 미니 프로젝트를 설명하는 글입니다. GDSC Ewha의 활동을 더 보고 싶다면 여기를 참고해주세요.
프로젝트의 깃허브 레포지토리 링크
Class Diagram을 기반으로 엔티티 만들기
이제 엔티티의 스펙이 모두 정해졌으니, FK, 필드, 클래스의 관계는 앞서 배운 대로 만들면 됩니다. 각 엔티티를 만드는 기본적인 탬플렛을 아래와 같이 보여드리겠습니다.
@Entity //필수: 클래스를 엔티티로 지정한다.
@Getter @Setter //필수: 롬복(lombok)에 의해 getter와 setter가 자동으로 만들어진다.
@Table(name = "memebers") //선택: 데이터베이스에서 “subjects"라는 이름의 테이블과 매핑됨을 지정한다.
public class Member {
@Id //필수: 엔티티의 기본 키(primary key)임을 나타낸다.
@GeneratedValue //필수: 기본 키 필드를 자동으로 생성하도록 지정한다.
@Column(name = "subject_id") //선택: id 필드를 데이터베이스의 “subject_id" 컬럼과 매핑한다.
private Long id;
//...기타 필드들...
}
위의 탬플렛을 바탕으로, 앞선 주차에서 설계한 클래스 다이어그램의 내용을 채워 넣으면 됩니다.
엔티티의 비즈니스 로직 구현
이제 엔티티의 비즈니스 로직을 한번 구현해보겠습니다. 구현해야 하는 주요 비즈니스 로직에는 다음과 같은 것들이 있습니다. (더 많이 있지만, 나중에 API를 만들 때 구현하도록 하겠습니다.)
- 포스트에 좋아요를 누르면 좋아요 수가 +1되고, 취소하면 -1된다.
이 로직은 두 가지 방식으로 구현할 수 있습니다.
첫 번째는 엔티티가 비즈니스 로직을 가지는 것입니다. 이렇게 객체 지향의 특성을 적극 활용하는 것을 도메인 모델 패턴
이라고 합니다. 두 번째로 엔티티에는 비즈니스 로직이 없고 서비스 계층에서 비즈니스 로직을 처리하는 방식이 있습니다. 이 방식은 트랜잭션 스크립트 패턴
이라고 합니다. 도메인 모델 패턴은 재사용성을 높일 수 있고(엔티티에 구현한 비즈니스 로직을 계속해서 사용 가능하므로), 트랜잭션 스크립트 패턴은 유연성을 높일 수 있습니다(서비스 계층에서 비즈니스 로직을 필요할 때마다 만들 수 있으므로)
한 프로젝트 안에서도 이 두 가지 패턴을 문맥을 고려하여 사용해줄 수 있습니다. 여기서는 엔티티에 비즈니스 로직을 넣어주는 도메인 모델 패턴을 선택하도록 하겠습니다. 그 이유는 첫째로 하나의 엔티티에 있는 필드(좋아요 수)만 변경해주기 때문이고, 둘째로 좋아요 수를 더하거나 취소하는 로직은 유연성이 높을 필요가 없는 기능이기 때문입니다.
아주 간단하게 Post 엔티티 내부에 비즈니스 로직을 구현해 보겠습니다.
/* 비즈니스 로직 */
public void addLikes(){
this.likes+=1;
}
public void deleteLikes(){
this.likes-=1;
}
참고: 웬만하면 @Setter 사용 X
엔티티 클래스 위에 @Setter
어노테이션을 사용하면 모든 필드의 생성자가 자동으로 만들어집니다. 그러나 @Setter
를 무분별하게 사용하면, 변경의 의도를 알 수 없는 변경이 많이 만들어집니다. 위의 예시는 좋아요 수를 변경해주므로 의도가 비교적 명확합니다. 하지만 유저의 이메일 정보가 Setter로 변경된다고 했을 때, 변경되는 이유가 유저가 새로 회원가입을 해서 생성된 것인지, 개인정보 수정을 통해 변경된 것인지 등을 추적하기 어렵습니다.
따라서 이 경우에는 createInfo()
, changeInfo()
,처럼 의도를 명확하게 알 수 있는 이름으로 메서드를 만들고, 해당 메서드를 사용하는 것이 좋습니다.
Entity를 GCP Cloud SQL에 연결
앞서 설계한 엔티티는 Class일 뿐, RDB 그 자체는 아닙니다. 엔티티(entity)는 데이터베이스의 테이블과 매핑되는 특별한 객체이고, 이를 실제 RDB에 연결해주어야 사용 가능합니다.
먼저 application.yml
파일에 다음 내용을 그대로 넣습니다. (application.yml 파일이 없고, application.properties 파일만 있다면 파일명을 수정해 yml 파일 형식으로 만듭니다.)
spring:
datasource:
url: jdbc:mysql://[Cloud SQL Public IP]:3306/[Database Name]?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
username: root # default
password: [Instance password]
driver-class-name: com.mysql.cj.jdbc.Driver
# hibernate
jpa:
show-sql: true
hibernate:
ddl-auto: update
위의 내용은 Cloud SQL에 연결할 엔드포인트, 유저명, 비밀번호, 드라이버 등을 명시한 설정파일입니다. 여기서 하나 중요한 점은, ddl-auto
옵션입니다. update
옵션은 연결된 DB와 비교하여 추가된 항목(엔티티 등)은 추가해주고, 변경이 없는 항목은 그대로 둡니다. 테스트할 때는 이렇게 만들고, 실제 배포시에는 none
(아무것도 안함)으로 두어 엔티티 변경 때문에 DB가 날아가지 않도록 만들어야 합니다(혹은 첫 배포 때만 update/create로 두고, 유지보수할 때는 none으로 두어야 합니다).
중요! ddl-auto: create
해당 옵션으로 두면 기존 데이터베이스의 테이블과 데이터가 싹 다 삭제되고 새롭게 테이블이 만들어집니다. 따라서 DB에 이미 많은 데이터를 저장했을 때는 절대 이 옵션을 쓰면 안됩니다. 만약 개발 중에 무언가가 매우 잘못되서 create 옵션으로 리셋해야 하는 경우라면, 반드시 DB에 필요한 데이터를 INSERT 해주는 SQL 문을 따로 저장해 놓고 DB를 리셋해야 합니다.
참고: DB가 꼬인 경우
아래와 비슷한 메세지가 출력될 수 있습니다. 이 경우에는 DB와 Springboot 간 불일치를 스스로 고치기 어려울 수 있으므로, ddl-auto: create
로 DB를 리셋할 수도 있습니다. 그러나 정상적으로 DB를 사용할 수 있는 경우라면 절대 ddl-auto: create
를 사용하지 않습니다.
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'member.PRIMARY'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:118) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1061) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1009) ~[mysql-connector-j-8.0.33.jar:8.0.33]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1320) ~[mysql-connector-j-8.0.33.jar:8.0.33]
Cloud SQL 인스턴스 생성
이제 Cloud SQL 인스턴스를 생성해주겠습니다. GCP 콘솔에서 Cloud SQL을 검색한 후, “무료 크레딧으로 인스턴스 만들기”를 선택합니다.
데이터베이스 엔진은 MySQL을 선택합니다.
Compute Engine API를 활성화하라고 나오면, 이를 활성화해줍니다.
인스턴스 스펙은 다음과 같이 설정합니다.
- 인스턴스 ID: 원하는 이름
- 비밀번호: 직접 원하는 비밀번호 설정
- 데이터베이스 버전: MySQL 8.0
- Cloud SQL 버전: Enterprise
- 버전 사전 설정: 개발
- 리전: 낮은 CO2 지역(여기서는 오리건)
- 인스턴스 맞춤설정 연결: 공개 IP(기본 설정)
나머지는 전부 기본 설정으로 두고, 인스턴스를 만들기 버튼을 클릭해 인스턴스를 생성해줍니다.
데이터베이스 생성
위에서 생성한 것은 DB의 인스턴스이지, 데이터베이스는 아닙니다. 데이터베이스를 만들어주기 위해서는 좌측 텝에서 데이터베이스를 선택한 후, 데이터베이스 만들기를 클릭합니다.
그 후 원하는 이름을 선택한 후, 문자 집합은 utf8로 두고, 대조는 기본 대조를 선택하고 데이터베이스를 만듭니다.
Local PC에서 접속 허용
좌측 텝에서 연결을 선택한 후, “네트워크 추가”에서 내 PC의 IP를 추가해야 합니다.
이름은 내 PC로, 네트워크는 내 PC의 공인 IP로 설정한 후 꼭 저장 버튼을 눌러 설정을 저장합니다.
참고로 다음 사이트에서 내 IP를 확인할 수 있습니다.
+) 이 방식은 내 IP가 바뀔 때마다(즉, 내가 사용하는 네트워크가 바뀔 때마다) 연결을 새롭게 수정해주어야 합니다. 따라서 개발 단계에서는 편의를 위해 0.0.0.0/0
으로 연결을 설정해도 됩니다. 단, 이 경우에는 DB 인스턴스의 공개 IP 주소가 외부에(특히 깃허브 공개 레포에) 노출되지 않도록 주의합니다.
application.yml 파일 수정
먼저 생성된 Cloud SQL의 공개 IP를 확인합니다.
그 후 application.yml 파일을 다음과 같이 수정해줍니다. 여기서 [Cloud SQL Public IP]
는 위에서 확인한 IP로 변경하고, [Database Name]
은 방금 설정한 DB 이름으로 변경하고, [Instance password]
은 처음에 설정한 비밀번호로 변경해줍니다. username은 디폴트로 root
가 됩니다.
spring:
datasource:
url: jdbc:mysql://[Cloud SQL Public IP]/[Database Name]?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
username: root # default
password: [Instance password]
driver-class-name: com.mysql.cj.jdbc.Driver
# hibernate
jpa:
show-sql: true
hibernate:
ddl-auto: update
Spring Boot 실행
정상적으로 연결된 상황이라면, 다음과 같이 ddl-auto: update
문에 의해서 테이블들이 생성되는 것을 볼 수 있습니다.
MySQL WorkBench로 DB 테이블 확인
이제 GCP에서 DB에 테이블들이 정상적으로 만들어진 상황이 맞는지 확인합니다. DB 테이블을 볼 수 있게 하는 여러 프로그램들이 있지만, 여기서는 간단하게 MySQL WorkBench로 확인하도록 합니다. 먼저 MySQL WorkBench를 열어 새로운 Connection을 만들어줍니다. Hostname
은 인스턴스의 공개 IP로, Username
은 root로, Password
는 인스턴스의 비밀번호로 설정합니다.
연결을 먼저 Test Connection으로 테스트하고, 정상적으로 연결되면 OK를 눌러 연결을 생성합니다. 그 후 연결을 클릭하면 DB 내부에 접속 가능하다. 이제 다음 쿼리 통해서 우리가 확인해야 하는 test 데이터베이스의 테이블들이 정상적으로 만들어졌는지 확인합니다. (참고로 쿼리는 ctrl+enter
로 하나씩 실행 가능하고, 위에 번개 모양의 아이콘을 클릭해 모든 쿼리를 전부 실행할 수도 있습니다.)
show databases;
use test;
show tables;
필드가 정상적으로 만들어졌는지를 확인하고 싶다면 다음과 같이 확인합니다. 그럼 해당 테이블의 모든 필드를 볼 수 있습니다.
show databases;
use test;
show tables;
select * from post;
다음 주차
이번 주차에서는 GCP에서 Cloud SQL을 만들고, Entity가 실제 RDB와 매핑되게 만들었습니다. 다음 시간에는 REST API의 기본 구조에 대해서 알아보고 API를 한눈에 볼 수 있는 방법에 대해서 알아보겠습니다.
'🗄️Backend > [GDSC] SpringBoot 프로젝트' 카테고리의 다른 글
Spring Boot 단기 프로젝트: 6. Make API(Auth, Circle) (0) | 2024.02.08 |
---|---|
Spring Boot 단기 프로젝트: 5. Make API(Profile, Posts) (0) | 2024.02.08 |
Spring Boot 단기 프로젝트: 4. Connect to GCP Cloud Storage & make Dto (0) | 2024.02.08 |
Spring Boot 단기 프로젝트: 3. RestController & Swagger-UI & Postman (0) | 2024.02.08 |
Spring Boot 단기 프로젝트: 1. Build RDB & Class Diagram (1) | 2024.02.08 |