Dev/Toy Project

개인프로젝트_Ukmedicine_4 ( 종료 )

린네의 2024. 3. 9. 19:38

이전 포스팅은 여기로

2024.02.25 - [개발/project] - 개인프로젝트_Ukmedicine_3

 
 

개발환경

Os :  MacOs 13.4
IDE :  intelliJ IDEA Edu  
FrameWork : springboot 3.2.2 / jpa / thymeleaf
Launguage java 17
DB : MariaDB 11.2.2
DB tool : DBeaver 23.3.0
FrontEnd Design :  Bootstrap 5.3.2
Github : https://github.com/gahyeonkwon/uk_medicine.git 

 
 
 

작업 목표

연관관계가 있는 엔티티 RecipeSpec로  데이터 삽입하기
@Setter, @AllConstructorm @NoArgsConstructor에 대한 고찰
Oracle Cloud 를 사용하여 배포하기 

 
 

  • 연관관계가 있는 엔티티  RecipeSpec로 데이터 삽입하기

RecipSpec 같은 경우 아래와 같이 엔티티를 정의했다.
 

@Entity
@Table(name = "medi_recipe_spec")
@Getter @Setter
@Slf4j
@ToString
@NoArgsConstructor
public class RecipeSpec {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "spec_id")
    private Long id;
    private Double materialMount;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "recipe_id")
    private Recipe recipe;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "material_id")
    private Material material;

    @Builder
    public RecipeSpec(Long recipeId) {
        this.recipe = Recipe.builder().id(recipeId).build();
    }


}

 

이때 Recipe 별로 들어가는 Material Mount의 양이 다르므로 Recipe와 RecipeSpec 사이에는 1 : N,  Material 과  RecipeSpec 사이에는 1:N 가 발생했다. 따라서 @ManyToOne 관계를 지정했고,  Recipe 와 Material의 데이터가 삭제될 경우 RecipeSpec 도 자동으로 삭제 될 수 있도록 CascadType.ALL  ( persist + remove 기능을 한 번에 지정하기 위해서 )을 지정했다. 
 
프런트에서 서버로 받아올 때부터 List <RecipeSpec> 형태로 가져오고 싶었는데 ( 엔티티 자체를 리스트처럼 ) 그런 기능은 아무리 찾아도 안 보여서 materialMount ( 재료별 용량 )과 materialId 만 list로 가져오고 서버단에서  List <RecipeSpec>을 생성한 뒤   saveAll 처리를 하는 방식으로 구현했다.
 
작업할 때 가장 많이 마주쳤던 에러는 다음과 같다.
 

  • RecipeSpec save 시 발생 - 에러 1

org.hibernate.PersistentObjectException: detached entity passed to persist: medicine.db.item.RecipeSpec
 

  • RecipeSpec save 시 발생 - 에러 2

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: medicine.db.item.RecipeSpec
 

  • 해결방안

검색하면 엔티티 종속성 문제가 있을 수 있으니 CascadeType.All을 지우라는 내용이 많이 나와서 지워 봤지만 변하는 건 없었다.
두 번째 가능성은 객체 생성에 문제가 있을 수 있다는 내용이 많아서 코드를 다시 보니  RecipeSpecId 값에 RecipeId를 넣어서 생성하고 있었다.  RecipeSpecId의 경우 자동 생성으로 설정해 놨기 때문에 문제가 발생한 거였다.  
 
Recipe 객체를 정상적으로 생성하도록 수정하고 나서는 문제없이 save 가 되는 걸 확인했다.
 
 
 

  • @Setter, @AllConstructor, @NoArgsConstructor에 대한 고찰

DTO 뿐만 아니라 Entity를 사용하게 되다 보니 늘 사용하는 @Setter, @AllConstructor, @NoArgsConstructor에 대해 고민이 생겼다.  이전글에서 builder 패턴사용 시에 @AllConstructor 이 강제됨에 따라 접근범위에 대해 어떻게 설정할지, 명시적 생성자에만 사용할 것인지 전체 클래스에 대해서 설정할 것인지.. 고민했었는데 여러 가지 글을 읽어보고 나름대로 정리한 결론은 다음과 같다.
 
 
 

  1.  dto에서는 크게 상관없는데 entity에서는 setter를 지양하자
  2. private로 선언한 생성자에 @Builder 패턴을 추가하여 사용하자 ( 권장사항이며 , 명시적 생성자에 해당한다 ) 
  3. @NoArgsConstructor를 아예 사용하지 않을 수는 없으므로 AccessLevel을 Protected로 설정하여 외부에서 무분별하게 사용되는 것을 방지하자 

 
 

  • Oracle Cloud를 사용하여 배포하기

 
 사이트는 다 만들었으니 이제 친구에게 만들었다고 보여줘야 하는데, 로컬에다 배포해 주고 쓰라고 할 수는 없으니까 외부에서도 편하게 사용할 수 있도록 클라우드서버를 찾았다. AWS는 예전에 회사 데모 사이트 올린다고 개인 카드 등록했다가 사용하지도 않은 것 같은데 뭐가 잘못 됐는지 돈이 나갔던 슬픈 기억이 있어서 배제하고 무료로 사용가능한 Oracle Cloud를 선택했다.
 
그런데... 무료라서 그런지 겁나 느리다. yum으로 파일 받다가 느린 게 아니라 서버가 멈춘 줄 알고 두세 번 정도 껐다 켰음 ㅎ
 


 
 
아무튼 사용 방법은 다음과 같다.
 
 

  • 회원가입을 하고 카드 등록을 한다

https://signup.cloud.oracle.com/?language=en&sourceType=:ow:o:p:feb:0916FreePageBannerButton&intcmp=:ow:o:p:feb:0916FreePageBannerButton

 

Oracle Cloud Free Tier Signup

signup.cloud.oracle.com

 
 
해당 사이트에 들어가서 단계별로 회원가입을 하고 나면 카드 등록을 하는데, 사이트 상태가 좋지는 않다. 나는 한 시간 정도 가입하려고 삽질을 하다가 페이지가 만료 됐다고 오류가 나서 포기하고 하루정도 있다가 다시 했는데 한 번에 됐다.   ( 브라우저도 바꿔보고 시크릿모드도 해보고 했지만 바뀌는 건 없었다 ㅠ
 

  • 로그인 후 Launch Resources > Create a VM instance 선택하여 instatnce 생성 

 
생성 시 옵션은 대부분 그냥 무료로 주는 걸 선택했다.  
 
 

  • secret / pub key download 

Instance를 생성하고 나면 secret / pub key 다운로드하는 항목이 있는데 둘 다 다운로드하여 준 뒤  Local에서 home 디렉터리의 ssh 폴더 하위에 이동시킨 뒤 할당 된  Public IP로 접속하면 된다.
 

.ssh 하위 모습

 

ssh opc@[public IP]

 
 
나는 원격툴을 안 쓰고 터미널에서 바로 시도했는데 초기 접속 시 아래와 같은 오류가 발생했다.
 
Permission denied publickey, gssapi-keyex, gssapi-with-mic

공홈에 연결하는 내용도 잘 나와 있는데 내가 정독을 안 해서 발생한 문제였다.   key에 400 권한을 주라길래 권한을 할당하고 아래와 같이 접속하니 정상적으로 됐다.
 

chmod 400 [privatekey file]

ssh -i [key_name].key opc@152.67.196.50

 
https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/connect-to-linux-instance.htm#top

 

Connecting to a Linux Instance

docs.oracle.com

 
 
 

  • yum으로 필요한 파일 설치 

서버에 접속이 정상적으로 됐다면 git을 설치해주자.  opc 계정이 관리자 권한을 사용할 수 있기 때문에  sudo 명령어를 사용하면 된다.
 

sudo yum install git

 
 
입력하니까  또 이런 오류가 떴다.
 
Failed loading plugin "osmsplugin": No module named 'librepo

 sudo dnf install python3-librepo -y

 
를 사용해서 해결했다.
 
참고로 설치하는데 생각보다 엄청 오래 걸린다. 서버가 멈춘 게 아니니까 화면에 반응이 없어도 기다려보는 것을 추천한다.
 
git을 설치한 뒤 mariadb와 java 17 도 설치해 줬다.  mariadb를 서비스로 시작하려면 systemctl을 사용해야 하는데 이때 mariadb-server 가 필요해서 함께 설치했다.

sudo yum install git
sudo yum install mariadb 
sudo yum install mariadb-server
sudo systemctl start mariadb
sudo yum install java-17-openjdk-devel.x86_64

 
db 서비스가 정상적으로 뜬 것을 확인하기 위해 port LISTEN 정보를 확인했다.

netstat -nao | grep 3306

 
 
 

  • db 마이그레이션

dbeaver에서 데이터베이스를 선택하고 우클릭하면 mysqldump를 사용할 수 있다.  ddl과 dml을 뽑아내서 저장한 뒤 git에 올렸다.
git clone을 실행하면 서버에 해당 파일이 올라오는데 mysql  서버에 접속해서  database를 만들어 주고  생성한 dump 파일을  실행해 줬다.
 

create database uk_medicine;
use uk_medicine;
source /home/opc/uk_medicine/db.sql


grant all privileges on uk_medicine.* to root@'localhost';
flush privileges;

 
 

  • git clone을 통해 배포하기 

/home/opc 하위에서 git clone 을 통해 프로젝트 폴더를 다운로드한 뒤 빌드를 실행했다.
 

git clone https://github.com/gahyeonkwon/uk_medicine.git
chmod +x gradlew 
./graldew build

 
 
빌드 도중 다음과 같이 오류가 발생했다.
 
java.lang.ClassNotFoundException: org.gradle.wrapper.GradleWrapperMain
 
테스트 코드 작성한 게 있었는데 서비스 로직을 변경하면서 남아있던 더미 테스트 코드가 빌드 시에 문제가 된 거라서 해당 코드를 주석처리 하고 재빌드하니 정상적으로 되는 것을 확인했다.
 
 
cd /home/opc/uk_medicine/build/libs 에 jar 파일이 정상적으로 뜬 것을 확인한 뒤 백그라운드로 실행시키기 위해 nohup java –jar [빌드된 jar 파일] &  로 실행 시켰다. 
 
 
 

  • 외부에서 접속하기 

ps로 프로세스가 뜬 것을 확인하고 내부에서 curl까지 호출해서 페이지가 정상적으로 뜨는 걸 봤는데 외부에서 접속이 안 됐다. 서버로 접근 로그가 찍히지 않아서 방화벽을 확인해 보니 방화이 문제였다. 아래와 같이 추가하니 정상접속 됐다.

sudo iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT

 
 
 

  • 그 외 오류

접속은 잘 됐는데 페이지 이동시 또 오류가 났다. 오류 좀 그만 발생해 주세요...
 

 
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/recipe/selectMaterial], template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.engine.TemplateManager.resolveTemplate(TemplateManager.java:869) ~[thymeleaf-3.1.2.RELEASE.jar!/:3.1.2.RELEASE]
 
 
이게 개발이랑 로컬에서는 안 나던 오류가 운영에서만 났는데, view 경로 매핑할 때 '/'를 제거하니까 정상적으로 됐다.
 
예를 들어 Controller에서 /home/test로 되어 있다면 home/test 로 변경해 줬다.
 
 
 
이로서 클라우드 서버에 성공적으로 배포했다. 하면서 자동배포의 필요성을 다시 한번 느꼈다. 
 
 
그래서 다음 포스팅은  Spring Cloud를 사용해서 CI/CD부터 젠킨스사용하는 것까지 정리해보려고 한다 ^0^  목표는 다다음주 이내로 정리하는 거다. 커밍 쑨 !
 
 
 

 
 
 
 

'Dev > Toy Project' 카테고리의 다른 글

개인프로젝트_Ukmedicine_3  (1) 2024.02.25
개인프로젝트_UKmedicine_2  (1) 2024.02.20
개인프로젝트_UKmedicine_1  (0) 2023.12.11