🙌잘못된 설명에 대한 지적이나 조언의 댓글을 환영합니다🙌

 

 

오류문구

org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade

원인

삭제나 업데이트 시 수정된 데이터가 제대로 반영되지 않은 상태에서 또 변경을 시도할 떄 발생하는 것으로 보인다.

데이터가 연관관계를 가지는 상황에서 원본 데이터를 수정하고 이를 참조하는 다른 엔티티의 컬럼을 변경하려고 시도할때 위와같은 오류가 발생됐다. 데이터 수정 순서를 변경하거나 아래 해결방안의 옵션을 추가하는 것으로 문제를 해결 할 수 있다.

해결방안

1. 엔티티에 orphanRemoval=true 추가

이 옵션은 삭제 등의 작업을 통해 joinColumn한 PK가 NULL 상태로 변경된 경우 해당 객체를 DELETE 해준다.

 

2. Cascade 옵션 삭제

이미 삭제한 코드를 Cascade 옵션이 부여된 상태에서 다시 참조하는 경우 해당 오류가 발생할 수 있다.

따라서 Cascade 옵션이 불필요하다면 이를 삭제하는 것도 방법이 될 수 있다. 

또는 코드의 순서를 적절하게 변경하는 것도 도움이 된다.

 

참고자료

https://stackoverflow.com/questions/18358407/org-hibernate-objectdeletedexception-deleted-object-would-be-re-saved-by-cascad

 

org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations):

i am getting the above error "org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): ". could someone help me what might be the...

stackoverflow.com

https://dev-elop.tistory.com/entry/JPA-orphanRemoval-%EC%9A%A9%EB%8F%84

 

JPA orphanRemoval 용도

@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER, orphanRemoval = true) 보통 1:N 관계 테이블 설정할때 저렇게 옵션을 추가해준다. 자식 엔티티의 변경이 있다면 JPA 에서 자식엔티티의 수정은 insert upda

dev-elop.tistory.com

 

DB data 추출하는 방법

현 회사에서 DB를 postgreSQL로 사용중인데, 데이터 제출을 위해 특정 테이블을 excel 파일로 추출해야했었다.

대부분 query tool을 통한 추출방법만 나와있길래, pgAdmin을 사용해 간단하게 추출하는 방법을 소개하고자 한다.

(윈도우, pgadmin4 사용 기준)

 

1. 데이터를 추출할 테이블을 조회한다.

아래 예시와 같이 조건없이 조회한 테이블도 추출이 가능하며, 특정한 조건을 붙여 조회한 결과 데이터도 추출이 가능하다.

 

2. pgAdmin 내 다운로드 버튼을 클릭한다. 

클릭하면 바로 내 컴퓨터로 조회한 데이터의 csv 파일이 저장된다.

 

3. csv로 저장된 파일을 연결프로그램 > excel로 열어서 확인한다. 

단, 이때 한글이 깨지거나 하는 문제가 발생한다면 csv 파일의 인코딩 타입을 변경하면 된다.

pgAdmin을 통해 저장된 csv파일을 메모장으로 열면 최조 인코딩 타입은 `UTF-8`로 지정되어있다.

메모장에서 다른 이름으로 저장 기능을 통해 인코딩 타입을 `ANSI`로 변경한 뒤 저장하고, excel로 열면 깨지지 않는다👍

+) Query tool을 통한 csv 파일 추출방법

COPY (
    select * from [테이블명] where [조건]
) TO '/home/postgres/[저장할 파일명].csv'
with csv header DELIMITER ',';

pgAdmin에서 query tool을 열고 위 쿼리문을 입력하면 /home/postgres 하위에 저장된다.

하지만 내 환경에서는 copy 쿼리 실행을 위한 exe 파일이 없다는 오류와 함께 동작하지 않아 GUI 방법을 사용했다.

아마 처음 postgresql을 설치할때 `Windows x86-32` 버전으로 잘못 설치해서 그런게 아닐까..

그것 때문인지 pgAdmin도 브라우저로 열리던데,, 이건 나중에 새로 설치하거나 해서 해결해봐야겠다

🙌잘못된 설명에 대한 지적이나 조언의 댓글을 환영합니다🙌

서론

사수님이 퇴사하고, 주로 비즈니스 로직을 다루는 백엔드 작업을 할 일이 많아졌다. 기존에는 코드를 마치 레시피처럼 외워서 개발해왔기 때문에 문제가 생겼을 때 스스로 디버깅하기가 어려웠다. 그래서 주 1회 퇴근 후 스터디를 통해 스프링 입문부터 공부하기로 결심했다. 오늘은 그 첫 번째 챕터인 웹개발 기초에 대해 알아보고자 한다.

 

스프링을 통한 웹 개발 방법

스프링으로 웹개발을 할 때 아래 세 가지 방법을 사용할 수 있다.

 

1. 정적컨텐츠 : HTML 파일 그대로 웹 브라우저에 전달

2. MVC와 템플릿 엔진 : 서버에서 HTML 파일을 동적으로 변환한 뒤 웹 브라우저에 전달

3. API : JSON으로 데이터를 클라이언트에 직접적으로 전달

 

각 방법의 특징과 스프링에서의 동작방식에 대해서 알아보자.

정적컨텐츠의 동작과 특징

정적컨텐츠는 클래스 경로나 ServletContext 루트에 있는 /static(or /public, /resources, META-INF/resources) 디렉터리에서 제공한다. 해당 폴더 내에 HTML 파일을 추가하는 경우 이를 그대로 반환한다는 특징이 있다. 별도의 설정 없이 스프링부트에서 기본적으로 제공하는 기능이지만 Spring MVC의 ResourceHttpRequestHandler를 사용하여 동작하기 때문에 자체 WebMvcConfigurer를 추가하고 addResourceHandlers 메소드를 재정의하여 해당 동작을 수정할 수도 있다.(하지만 이렇게 수정해서 사용할 이유가 없음..) 또한, 기본적으로 리소스는 /**에 mapping되며, 이 경로를 수정하고 싶은 경우 spring.mvc.static-path-pattern 속성을 변경하여 mapping 경로를 수정하는 것도 가능하다. 

 

스프링부트로 실행한 웹 브라우저에서 정적컨텐츠인 HTML파일을 표시하기 위해 톰캣 서버로 요청을 보내면, 스프링부트의 스프링컨테이너에서는 해당 url과 mapping 된 controller를 찾는다. 하지만 controller가 없는 정적컨텐츠이기 때문에, static 폴더 등을 살펴보고 동일한 이름의 HTML파일을 브라우저로 전달하는 방식으로 동작한다. 

 

출처: 김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의

 

MVC와 템플릿 엔진의 동작과 특징

이 방법의 특징은 한번 렌더링 한 HTML을 브라우저에 전달한다는 점이다. 이해를 위해 MVC란 무엇인지에 대해 먼저 알아보자. MVC는 Model, View, Controller의 구성을 뜻하며, 역할에 따라 기능을 분리하여 제공하기 위해 사용한다. Model은 controller가 화면에 표시해야 하는 것들을 정리해서 담는 곳이며, 이를 view에 전달하는 역할을 한다. View는 오로지 화면을 그리는 일만을 담당하며, Controller는 비즈니스 로직을 담당하며, 요청을 받았을 때 화면 뒷단에서 처리해야 하는 일들을 담는 역할이다. 여기서 View가 php, jsp 등 템플릿엔진을 통해 Model, Controller의 정보를 받아 HTML을 다시 렌더링 하고 그 결과를 브라우저에 전달하도록 개발하면 된다. 이처럼 템플릿엔진을 MVC방식으로 쪼개서 동작하도록 만들었다면 이 방법을 사용한 것이라고 이야기할 수 있다. 

 

웹 브라우저의 요청을 톰캣서버로 보냈을 때 스프링컨테이너에서 가장 먼저 확인하는 Controller단에서 mapping된 정보가 존재한다면, 컨트롤러의 동작을 먼저 수행하고 이를 바탕으로 viewResolver가 HTML파일을 렌더링해 변환을 마친 HTML을 웹 브라우저로 전달하는 방식으로 동작한다.

 

출처: 김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의

 

API의 동작과 특징

API 방식은 데이터를 바로 내려준다는 특징을 가진다. 이 방식을 사용하기 위해서는 Controller에서 @ResponseBody라는 어노테이션을 사용해야 하는데, 이는 해당 데이터를 HTML의 <Body> 태그 내부로 전달한다는 뜻이 아닌, HTTP 내 응답 Body부분에 해당 데이터를 직접 넘겨주겠다고 정하는 것을 뜻한다. 따라서 return 값이 view 템플릿을 거치는 것이 아니라 데이터 형식 그대로 전달된다는 특징이 있다. 

 

웹 브라우저를 통해 톰캣서버로 전달된 요청은 MVC 때와 동일하게 스프링컨테이너 내 mapping된 Controller로 전달되고, 해당 Controller에 적용된 @ResponseBody가 파라미터를 통해 생성한 객체를 그대로 return으로 반환한다. 이때 HttpMessageConverter가 JsonConverter를 통해 객체를 JSON 형태로 변환하여 이를 웹 브라우저에 전달하는 방식으로 동작한다.

 

출처: 김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의

@Controller
public class HelloController {
 
    @GetMapping("hello-api")
    @ResponseBody
    public Hello helloApi(@RequestParam("name") String name) {
        Hello hello = new Hello();
        hello.setName(name);
        return hello;
    }

    static class Hello {
        private String name;

        public String getName() {
        	return name;
        }

        public void setName(String name) {
        	this.name = name;
        }
    }
}

 

정리하며,

이렇게 오늘은 스프링을 통해 웹개발하기 위한 방법 세 가지에 대해 공부했다. 부끄럽지만 평소 내가 사용하고 있는 방식이 API인지도 정확히 모르고 사용해 왔었고, view 구성을 위해 model에 데이터를 넘겨주는 방법도 이제야 제대로 알게 된 것 같다. 아직 기본적인 내용 밖에 담지 못했지만, 점차 학습하며 각 방법을 적절하게 활용하는 개발자가 되어야겠다💪 

 

Reference

 

Spring Boot Features

Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and Servlet-based web applications. It occurs as part of closing the application context and is performed in the earliest

docs.spring.io

 

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

본 글은 초보개발자의 입장에서 작성된 글이며, 잘못된 설명이 포함될 가능성이 있습니다.

🙌잘못된 설명에 대한 지적이나 조언의 댓글을 환영합니다🙌

 


 

+ 22.04.28 오전 9시 내용 추가

어제 오후 6시 경 해당 부분을 수정하여 0.27.2 버전이 새로 릴리즈되었다. 

하지만 최신 버전을 적용했을 때 동일한 이슈가 아직도 발생된다는 사실을 확인하였으며,

안정화된 버전인 0.26.1을 계속해서 사용하기로 했다. 

 

Axios 개발자분이 0.27.0버전에서 데이터를 자동으로 처리하는 방식에 대한 내용을 해당 링크에서 확인할 수 있다고 언급해주셔서 추가해둔다. 시간될때 한번 더 확인해보고, 확실히 이해해야겠다. 

https://github.com/axios/axios#form-data

 

GitHub - axios/axios: Promise based HTTP client for the browser and node.js

Promise based HTTP client for the browser and node.js - GitHub - axios/axios: Promise based HTTP client for the browser and node.js

github.com

 


 

오류문구

POST http:://~~~~ 403

 

발생상황

어제 오후 3시까지만 해도 잘됐던 코드와 기능들이 오후 6시경 갑자기 동작하지 않는 에러 발생

form data를 axios.post로 넘겨주는 기능이 동작하지 않고 403 오류를 발생시킴

 

해결방안

(해결방안만 보고 싶은 분들을 위해 먼저 적어둡니다)

현재 프로젝트에는 unpkg CDN으로 Axios를 사용하는 중이었고, 현재 버전에 문제가 있어 이전 버전으로 연결시켰다.

<!-- 변경전 unpkg CDN 코드 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<!-- 이전 버전연결을 위한 unpkg CDN 코드 -->
<script src="https://unpkg.com/axios@0.26.1/dist/axios.min.js"></script>

 

 이것저것 코드들을 많이 수정한 터라 도대체 어디에서 문제가 생긴 건지 혼란스러웠다. 커밋하기 전에 분명 테스트해보고 동작이 정상적으로 된다는 것을 확인하긴 했지만, 그때까지만 해도 내가 작성한 코드가 완전무결하다는 것에 대한 자신감이 없었기 때문에 나부터 의심해볼 수밖에 없었다. 게다가 특정한 한 페이지에서만 동작이 안 되는 줄로 알고 있어서 이전에 동작이 잘 되었던 코드로 revert 해보기도 하고, 뭐가 문제인지 코드들을 다 뜯어보고 있었는데, 다른 페이지를 작업하고 있던 동료에게도 같은 문제가 발생하는 게 아닌가..! 그래서 코드의 문제는 아니라고 판명하고 왜 이런 오류가 발생하는지를 찾아내기 위해 엄청난 구글링을 시작했다. (사실 내 코드의 문제가 아님을 깨닫기까지 장장 4-5시간을 소모한 것 같다..)

 

 코드의 문제가 아님을 깨닫고 403 Error가 언제 발생하는지, 그리고 AxiosError가 나오는 상황은 도대체 어떤 상황인지를 파악하기 위해 수없이 많은 블로그 글을 보고 또 봤다. 그래도 해결이 안 돼서 마지막 방법으로 찾은 것이 바로 Axios 공식 깃헙이었는데, 우연히 거기서 정답을 찾게 되었다. (아, 이래서 많은 개발자분들이 공식문서 보는 습관부터 들이라고 하신 거구나 하고 이해하게 되는 순간이었다.)

 

 나도 내가 왜 그런 행동을 했는지는 모르겠지만, 무의식적으로 공식 깃헙의 History를 먼저 확인해 보았다. 그럴 가능성은 희박하겠지만, 혹시나 그 사이에 버전 업데이트가 되면서 제대로 동작하던 기능들에 문제가 생긴 것은 아닌지를 가장 먼저 체크해보고 싶었던 것 같다. 커밋 이력을 살펴보니 동작이 안됐을 것으로 추정되는 시각에 새로운 버전이 업데이트되었다는 사실을 알게 됐다. 이때부터였다. 막막했던 이 403 에러의 해결의 실마리가 보이기 시작한 것이 :)

 

 그다음에는 Issue 탭을 들어가서 최근에 올라온 이슈 중 우리가 겪은 상황과 비슷한 내용이 있는지를 탐색했다. 그렇게 찾은 것이 바로 이 이슈다.

https://github.com/axios/axios/issues/4638

 

Axios 0.27.1 has a pretty serious problem · Issue #4638 · axios/axios

Describe the bug There are some people whose production environment is using the CDN introduction。 Using jsDelivr CDN:<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"...

github.com

 

 읽어보니 지금 내가 겪은 상황이랑 같았다. 0.27.1버전으로 업데이트 된 후에 axios.post로 formData를 받아오는 동작이 안된다는 내용이었는데, 이 이슈 글로 인해 동작이 안됐던 문제가 내 코드로 인한 것이 아닌, axios 버전 업데이트로 인한 것임이 분명하게 밝혀져 조금은 속 시원하기도 했다. 심지어 이전 버전의 CDN을 사용하는 방법까지 하단 코멘트로 친절하게 달아놓으셔서 당장 문제의 해결이 필요한 상황에서 큰 도움이 되었다. 최신 버전의 CDN을 사용하는 코드를 변경하니 바로 해결이 됐고, 조용한 사무실 한 켠에서 동료와 이슈 해결의 기쁨을 누렸다. 어떻게 보면 오늘 하루를 통째로 이것 때문에 날린 셈인데, 해결했다는 성취감이 이렇게 클 줄이야.. 이 맛에 개발하지😋

 

 

느낀점

 막상 정리하고 보니 이렇게까지 기뻐할 문제는 아닌 것 같기도 하다만, 그래도 답이 없다고 생각했던 문제를 끈질기게 포기하지 않고 직면한 결과를 해결이라는 즐거움으로 보상받은 것 같다. 역시 세상에 답 없는 문제는 없고, 내가 겪은 문제 중 대부분은 수많은 선배 개발자가 이미 겪고 해결해낸 문제라는 것을 오늘 다시 한번 깨닫게 된 것 같다.  

 

 또, 공식문서의 필요성과 영어로 된 문서를 읽어야 하는 필요성에 대해 절실하게 느끼게 된 계기였다. 사실 공식 깃헙을 조금만 더 빨리 봤었더라면 좋았을 텐데, 본능적으로 한글로 된 자료들만 찾아보다 보니 문제를 해결하는 데에 더 많은 시간이 소요된 것 같다. 앞으로는 공식문서를 먼저 보는 습관을 들여야겠다 ( •̀ ω •́ )✧

 

ps. 403 에러 해결에 어려움을 겪고 있다고 알렸을 때, 같은 회사도 아니지만 구글링에 함께 동참해준 동료 개발자분들에게 이 자리를 빌려 감사의 마음을 전해 본다. 그럼 이만~ ο(=•ω<=)ρ⌒☆

 

오류문구

Context initialized 이벤트를 [org.springframework.web.context.ContextLoaderListener] 클래스의 인스턴스인 리스너에 전송하는 동안 예외 발생

이러한 오류 문구가 출력되었다

 

발생상황

평소와 다름없이 서버를 켜고 작업을 시작하려던 차, 갑자기 이런 오류가 뜨면서 서버 실행이 안됐다. 

어제 퇴근하기 전까지만 했어도 잘 됐던 서버고, 출근하자마자 실행시킨거라 파일을 수정한 것도 없는데 안됐다.

도대체 왜 안되는지 감을 잡을 수도 없는 상황이었다 ╮(╯_╰)╭

 

해결방안

내가 사용하는 STS4 에디터를 기준으로 설명되는 점인 것을 미리 알린다.

1. 프로젝트 우클릭 > Properties 클릭

2. Development Assembly > Maven Dependencies > 하단에 Apply(또는 Apply and Close)

3. 톰캣서버 재실행 하면 정상적으로 동작~!

 

처음 보는 오류에 당황해서 이것저것 구글링을 하며 다양한 방법을 시도해보려고 했으나,

혹시나 내가 프로젝트를 망쳐버리면 어쩌지 하는 두려움에 파일을 크게 건드리지 않는 선에서 시도해볼 수 있는 방법을 택해 이런저런 것들을 해봤다. 결국 해결이 안돼서 마지막 수단으로 컴퓨터 재부팅을 해봤는데 해결됐었던 오류였다.

그때까지는 몰랐지,, 이 오류녀석과의 끈질긴 인연이 시작됐다는 사실을 말이다🤦‍♀️

 

처음 위 오류를 접한게 3월 16일, 그리고 또 이 오류 녀석을 만난게 4월 12일이었다.

그래도 한 번 만나본 오류라고 의기양양해서 컴퓨터를 재부팅했는데도 서버 실행이 안됐다.

아니 왜 저번에는 됐는데~ 이번에는 또 안되는거냐고오.. 하면서 구글링에 다시 착수했다.

그래도 한달동안 이것저것 파일들을 만져보면서 프로젝트 수정에 대한 두려움이 조금 사라져서 그랬는지는 몰라도,

가장 효과적일 것으로 보였던 톰캣 서버 삭제 후 재설치 방법을 택해 수행해보았다.

다행히 다시 설치하니 제대로 또 동작했는데, 문제는 그 다음날에 발생했다.

 

다음날이 되니 또 이 짜식이 실행도 안되고 똑같은 오류를 내뱉는거 아니겠나..... _(:з)∠)_

그래서 톰캣 서버 삭제후 재설치를 하면 또 정상적으로 실행이 되고,,, 간헐적으로 이런 현상을 반복했어야했다.

 

그리고 오늘인 4월 20일 한동안 잠잠했던 이 오류가 또 아침부터 나를 괴롭혔다.

변경한 것도 없고 어제 퇴근하기 전까지만 했어도 잘 됐었잖아..😢 라고 생각하기도 잠시

도대체 같은 오류가 왜 몇번씩 발생하는걸까 하는 근본적인 궁금함이 생겨서 다시 구글링해 보았다.

 

그렇게 발견한 새로운 해결방법이 바로 위에 적어놓은 방법이다.

제게 새로운 해결책을 제시해주신 아래 사이트의 질문자님과 작성자님,, 계신 곳으로 절하겠습니다🙇‍♀️🙇‍♀️

https://www.egovframe.go.kr/home/faqinfo/faqinfoRead.do?menuNo=68&faqId=FAQ_0000000000000361 

 

자주 묻는 질문 | 표준프레임워크 포털 eGovFrame

처리중입니다. 잠시만 기다려주십시오.

www.egovframe.go.kr

항상 만수무강하시고 사는동안 행복하십쇼 ο(=•ω<=)ρ⌒☆

 

느낀점

이 방법을 오늘 처음 시도해본 거라 앞으로 해당 오류가 또 발생할지 아닐지를 알 수는 없지만, 그래도 하나의 해결방법에 그치지 않고 다양한 해결법을 시도해보고 조금이나마 자바 프로젝트랑 친해진 기분이 들어서 좀 뿌듯한 것 같다. 만약 또 이 오류가 발생한다면 계속해서 방법을 수정해볼 예정이다! 많관부 많관부~(❁´◡`❁)

+ Recent posts