전체 글 106

spring boot + aws s3

s3 버킷을 생성하는 방법은 이 블로그에 친절하게 나와 있어서 도움이 많이 됐다. https://devlog-wjdrbs96.tistory.com/323 [Spring] Spring Boot AWS S3 사진 업로드 하는 법 Spring Boot S3 File Upload 하는 법 이번 글에서는 Spring Boot 로 AWS S3 로 File Upload 하는 법에 대해서 정리해보겠습니다. 먼저 AWS S3 Bucket 생성을 하겠습니다. AWS S3 Bucket 생성 그리고 권한 탭을 들.. devlog-wjdrbs96.tistory.com 그리고 전 배민 개발팀장님의 블로그에서도 해당 주제의 포스팅을 찾을 수 있었다. https://jojoldu.tistory.com/300 SpringBoot & ..

nuxt에서 vue 파일에 name을 사용하는 건 의미가 없다

일반 vue를 사용할 때처럼 페이지 vue 파일의 태그 안에 name: xxx와 같이 name을 지정해줬다. NuxtLink의 to props에 이 name을 넘겨줘봤더니 개발자 콘솔에 경고가 뜨고 정상 작동하지 않았다. 알아보니 nuxt는 스스로 name을 지정한다. pages 폴더 하위에 projects 폴더 하위에 _id.vue를 뒀다면 nuxt는 이 _id.vue에 projects-id라는 name을 지정해준다. 내가 다른 name으로 설정해도 무시한다. 이 글 덕분에 알게 되었다. https://stackoverflow.com/questions/50596345/dynamic-routing-is-not-working-in-nuxt

간과하기 쉬운 자바 문법

자바 문법은 원시 타입의 래퍼 객체가 있다. long을 감싸주는 Long 클래스, int를 감싸주는 Integer 클래스 기타 등등. 많다. 래퍼 객체는 저장된 원시 타입의 값이 같은 지 비교할 때 ==이 아닌 equals를 써야만 한다. 원시 타입 래퍼 클래스 또한 클래스이기 때문에 == 비교 시 주소를 가지고 비교하게 된다. 여러 언어를 자주 사용한다면 헷갈리기 쉬운 문법이다. 프로젝트 개발 도중에도 session에 저장된 유저의 id와 수정하려는 프로젝트의 제안자 id를 비교할 때 ==을 썼었다. 하지만 저기 getId()의 반환 타입이 Long이라는 함정. if (targetProject.getProposer().getId() != sessionUser.getId()) { throw new Not..

CascadeType.REMOVE와 orpahnRemoval=True

엔티티의 리스트 필드에 CascadeType.ALL을 적용한 뒤 손수 remove를 호출해줬으나 제거에 실패했다. 아래 사진처럼 말이다. 같은 stack_id, user_id 값이 두 개 있다. CascadeType.REMOVE는 부모 엔티티가 완전히 제거됐을 때 자식 엔티티 또한 제거되는 역할을 한다. 단지 리스트에서 제거한다고 자식 엔티티가 제거되는 것이 아니다. 그래서 orphanRemoal = True를 추가해줬다. 이번에는 또 다른 에러를 만나게 된다. A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance userService에서 list를 변경해주는 코드에 문제가 있었..

@Entity 적용한 클래스에 getter가 없으면 jackson이 파싱을 못 하셔

제목 그대로다 @Entity 적용한 클래스에 getter가 없으면 jackson이 파싱을 못 해서 해당 객체의 필드들을 읽을 수 없다. jackson이 내부적으로 getXX를 호출하는 모양이다. 다시는 이걸로 헤매지 않으리라. getter가 없으면 -> jackson이 객체의 필드들을 읽지 못 한다 -> 객체를 json으로 변환해주지 못 한다. (빈 json을 만든다)

jpa 사용 시 양방향 연관 관계에 모두 @JsonIgnore 처리

Stack과 partStack은 oneToMany, ManyToOne 양방향 연관 관계를 맺고 있었다. 때문에 사용자 정보를 가져올 때 getMyStacks()만 호출했는데도 partStack 데이터까지 읽어왔다. 전혀 필요 없는 데이터고 요청도 하지 않았는데 말이다. 더 큰 문제는 User가 UserPart를 호출하고 UserPart가 또 User를 호출하면서 무한 루프에 빠진 상황이었다. 그렇다. jackson의 처리 방식 때문이다. 나는 getXXX를 통해 특정 테이블의 정보만 얻어오고 싶었는데 그 테이블에 매핑된 객체에서 다른 테이블의 객체를 참조하고 있었다. @XXToMany는 기본 fetch 방식이 LAZY이기 때문에 많은 연관된 데이터를 읽어오도록 요청하지 않고 단지 프록시 객체를 받아온다...

cors 빠져나가기

첫 화면의 데이터는 서버로부터 잘 받아와서 화면에 뿌려줬다. 그런데 nickname 관련 api 요청은 cors에 의해 막혔다. 왜 이런 일이 벌어질까? 스택 리스트와 프로젝트 리스트는 nuxt의 fetch call을 통해 api를 요청한다. nuxt 공식 문서를 통해 살펴보면 fetch()와 async()는 서버 사이드 렌더링 시점에 호출된다. 참고로 axios에 proxy 설정은 안 해준 상태이다. 서버 사이드 렌더링 때 가져온 api 결과는 브라우저에서 cors 보호를 안 해주는 것이다. 보면 nickname 호출은 클라이언트 사이드 렌더링 시점에 호출되는 mounted() 시에 이루어진다. 클라이언트 사이드 렌더링 중에 이루어진 호출은 cors 검사가 이루어진다. 이제 저 mountd() 훅 대신..

.nuxt 하위 파일을 변경하지 마세요

nuxt를 사용하면서 전역 스타일을 주기 위해 .nuxt > layouts > default.vue에 직접 style 태그를 작성했었다. 그런데 분명 저장을 했는데도 dev 모드를 종료하고 다시 켜면 style이 기본값으로 돌아가 있었다. 나는 .nuxt가 의미하는 바를 몰랐던 것이다. .nuxt는 nuxt가 프로젝트를 빌드하면서 생성되는 폴더이다. 그러니 매번 새로 시작할 때마다 .nuxt에 작성해줬던 내용들은 사라진다. 전역 css를 만들고 싶으면 nuxt.config.js의 css에 파일명을 추가해줘야 하고, 플러그인을 사용하고 싶다면 마찬가지로 nuxt.config.js에서 plugins에 파일을 지정해주면 된다.

처음 테스트 코드를 작성하며 겪었던 문제들

spring boot의 controller 클래스의 테스트 코드를 만들어봤다. 이게 내 첫 테스트 코드 경험이다. mockito 쓰는 게 아직 낯설지만 테스트 코드를 작성하다 만났던 에러들을 기록한다. @WebMvcTest(UserApiController.class) class UserApiControllerTest { @Autowired private MockMvc mvc; @MockBean private UserService userService; private Gson gson = new Gson(); ... } 테스트 케이스 위에 @WebMvcTest 를 써주는 것만으로도 무사히 테스트가 통과됐다. 처음에는 검색 시 너무 많은 애노테이션이 나와서 이것저것 다 써봤는데 이거 하나면 충분했다. 주의할..

사용자 스토리로 요구사항 정리해보기

프로젝트 개발에 무작정 돌입했지만 요구사항을 한번 싹 정리해놓고 개발하는 게 좋겠다는 생각이 들었다. 기능을 하나 개발하고 보니 추가적으로 필요한 매개변수가 생각나는 문제점이 존재해 이런 생각에 이르렀다. 그리고 다음에는 무엇을 개발해야 할 지 생각하는 게 조금 오래 걸리는 감이 있어서 요구사항 정리의 필요성을 느꼈다. 스프링 5와 vue.js2로 시작하는 모던 웹 애플리케이션 개발 책을 보고 있는데 요구사항 분석부터 데이터 모델링, 코드 설계, api 설계, 개발, 배포까지 전 과정을 정리해 놓은 좋은 리소스이다. 백엔드 개발을 공부할 때 많이 보게 됐던 디자인 패턴과 용어들을 잘 정리해준 책이다. 이 포스트에서는 사용자 스토리로 요구사항을 작성하는 부분을 내 프로젝트에 적용해보았다. 사용자 스토리란 ..