MEETPLUS 프로젝트 당시 프론트에서 HTTP GET 요청을 했는데 CORS 오류가 발생했습니다. 클라이언트 모든 요청은 gateway를 통해 들어오고 있었고, Spring Cloud Gateway 서버가 gateway 역할을 담당하고 있었습니다.gateway에서는 MSA의 각 도메인 서버별로 구체적인 경로와 필터를 지정해 준 상태였습니다.그런데 왜 CORS 오류가 발생했을까요? 이는 preflight 요청때문이었습니다.preflight 요청이란, 브라우저가 서버에 요청을 보내기 전에 이 요청을 허용할지를 확인하기 위해 보내는 예비 요청입니다.preflight 요청이 발생하는 조건1. CORS 요청이 "단순 요청(Simple Request)" 조건을 충족하지 않는 경우단순 요청의 조건은 다음과 같습니..
spring boot 애플리케이션 환경설정을 Spring Cloud Config로 관리하고 있었습니다.환경설정 파일(ex. application.yml)은 github private repository에 저장하고 Spring Cloud Config가 애플리케이션 실행 시 맞는 환경설정 파일을 가져와 적용해줍니다. 그런데 환경설정을 수정할때마다 변경사항을 적용하기 위해 /actuator/refresh API 를 호출해야했습니다.이는 환경설정이 빈번하게 수정되는 개발기간에 상당히 귀찮았고, 게다가 MSA에서 여러 애플리케이션들의 auctuator API를 호출하는 것도 비효율적이었습니다. 요구사항은 다음과 같았습니다.1. 레포지토리에 push하면 자동으로 수정된 환경설정이 적용될 것2. 한번의 동작으로 여러..
모든 GrammarBook에서 각 book마다 연관된 Grammar 개수를 조회하는 기능이 있습니다. 해당 코드는 다음과 같습니다.@Transactional(readOnly = true)public List getGrammarNumOfAllGrammarBooks() { List responseDtos = new ArrayList(); for (GrammarBook grammarBook : this.grammarBookRepository.findAll()) { responseDtos.add( GrammarNumOfGrammarBookDto.builder() .id(grammarBook.getId()) .name(grammarBook.getN..
사용자가 문법 퀴즈를 풀고 틀린 문제를 저장하는 테이블(UserGrammarStatus)이 있습니다. 이 테이블에서 한 사용자가 틀린 모든 문법 문제를 조회하는 기능에서 N+1이 발생했습니다. 해당 기능은 아래 메서드이며, 사용자 이름으로 SiteUser 테이블에서 id를 찾아, UserGrammarStatus 테이블에서 해당 id의 grammar들을 조회합니다. @Transactional(readOnly = true) public WrongGrammarsResponseDto getUserWrongGrammars(String userName) { Long userId = getUserIdByUserName(userName); List userGrammarStatuses = this.userGrammarS..
“자바 ORM 표준 JPA 프로그래밍” 책으로 N+1에 대해 공부했습니다. 이후 기존 조회 로직들을 다시 테스트해보니 N+1 문제가 발생하는게 생각보다 많았습니다. 그래서 이것들을 모두 해결했고, 시리즈로 엮었습니다. 다음 코드는 Grammar 1개를 조회할때 연관된 GrammarBook과 GrammarExample까지 조회하는 service 메서드입니다. public GrammarDto getGrammar(Long id) { Grammar grammar = getGrammarById(id); return GrammarDto.builder() .id(id) .sentence(grammar.getSentence()) .grammarBookName(grammar.getGrammarBook().getName(..
오류 발견 아래 코드를 테스트하던 중 LazyInitializationException이 발생했습니다. public GrammarBookResponseDto getUserWrongGrammarBook(String userName, String grammarBookName) { Long userId = getUserIdByUserName(userName); List userGrammarStatuses = this.userGrammarStatusRepository.findByUserId(userId); Long grammarBookId = this.grammarBookService.getGrammarBookIdByGrammarBookName(grammarBookName); List grammarDtos = ..
문제 상황현재 Grammar와 GrammarBook를 N:1 양방향 연관관계로 설정한 상태입니다. 그래서 GrammarBook에 다음과 같이 mappedBy 설정으로 GrammarBook은 연관관계의 주인이 아님을 명시해뒀습니다.@OneToMany(mappedBy = "grammarBook", cascade = CascadeType.ALL) @OrderColumn(name = "id") private List grammars = new ArrayList();그리고 GrammarBook엔티티 객체에서 grammars로 size()를 호출하니 수많은 Grammar엔티티 값과 함께 수많은 null값이 채워져 반환되었습니다. [null, null, null, null, null, null, null, null,..
UserGrammarStatus와 SiteUser는 N : 1 단방향 연관 관계를 맺고 있습니다.UserGrammarStatus 엔티티에서 referencedColumnName = "user_name"를 지정해주어 SiteUser 테이블의 userName 컬럼을 참조하게 했습니다.//UserGrammarStatus ... @ManyToOne @JoinColumn(name = "user_name", referencedColumnName = "user_name") private SiteUser user; ...그리고 userName값으로 UserGrammarStatus를 조회하는 JPQL을 직접 작성했습니다.//UserGrammarStatusRepository ... @Query("select ugs fro..
문제 상황 GrammarBook 엔티티 한 개를 조회할때 Grammar를 fetch join하고 Grammar안에서 GrammarExample도 fetch join하는 한 방 쿼리를 JPQL로 작성했습니다. //GrammarBookRepository.java @Query("select gb from GrammarBook gb join fetch gb.grammars g join fetch g.examples where gb.id = :id") Optional findGrammarBookById(@Param("id") Long id); 위 JPQL을 사용한 서비스 메서드를 테스트했더니 org.hibernate.loader.MultipleBagFetchException: cannot simultaneousl..
리액트와 스프링부트를 통합 빌드했기 때문에 스프링부트 테스트 시 항상 리액트 앱을 빌드했습니다. 이때문에 빌드 시간이 너무 오래 걸려 간단한 테스트에도 시간을 많이 뺐겼습니다. 그래서 build.gradle에 테스트할때만 리액트 앱을 빌드하지 않도록 설정을 추가했습니다.//build.gradle ... //테스트할때만 리액트 앱 빌드 안함 def skipReactBuild = project.hasProperty('skipReactBuild') tasks.matching { it.name in ['installReact', 'buildReact', 'copyReactBuildFiles'] }.configureEach { onlyIf { !project.hasProperty('skipReactBuild') ..