묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
(해결) 각 테스트가 상태를 공유하지 않습니다.
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]"회원 리포지토리 테스트 케이스 작성" 강의에서 MemoryMemberRepositoryTest를 작성하시며 @AfterEach를 통해 각 테스트 케이스가 끝날 때 마다 repository 필드의 상태를 초기화해야 한다고 말씀하셨습니다.그런데 저는 별 다른 오류가 나타나진 않아서 다음과 같이 작성해봤습니다.@AfterEach void afterEach() { System.out.println(repository.hashCode()); }그랬더니 각 repository의 해쉬가 서로 다르더라구요. MemoryMemberRepository 구현 내부에서 상태를 출력하게 해보면, 매 테스트 케이스마다 새로운 상태가 되는 것으로 보입니다.제가 잘못 작성한 부분이 있는 걸까요? 아니면 스프링, 라이브러리 버전의 문제인가요? 전체 코드는 다음과 같습니다. 읽어주셔서 감사합니다.// MemoryMemberRepository.javapackage hello.hello_spring.repository; import hello.hello_spring.domain.Member; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; public final class MemoryMemberRepository implements MemberRepository { private final Map<Long, Member> store = new HashMap<>(); private long sequence = 0; @Override public Member save(Member member) { member.setId(++sequence); store.put(member.getId(), member); return member; } @Override public Optional<Member> findById(Long id) { return Optional.ofNullable(store.get(id)); } @Override public Optional<Member> findByName(String name) { return store.values() .stream() .filter(member -> member.getName().equals(name)) .findAny(); } @Override public List<Member> findAll() { return store.values() .stream() .toList(); } public void clearStore() { store.clear(); } }// MemoryMemberRepositoryTest.javapackage hello.hello_spring.repository; import hello.hello_spring.domain.Member; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; class MemoryMemberRepositoryTest { MemoryMemberRepository repository = new MemoryMemberRepository(); @AfterEach void afterEach() { System.out.println(repository.hashCode()); } @Test void save() { var member = new Member(); member.setName("spring1"); repository.save(member); var result = repository.findById(member.getId()).get(); assertThat(member).isEqualTo(result); } @Test void findByName() { Member member1 = new Member(); member1.setName("spring1"); repository.save(member1); Member member2 = new Member(); member2.setName("spring2"); repository.save(member2); var result = repository.findByName("spring1").get(); assertThat(result).isEqualTo(member1); } @Test void findAll() { Member member1 = new Member(); member1.setName("spring1"); repository.save(member1); Member member2 = new Member(); member2.setName("spring2"); repository.save(member2); var result = repository.findAll(); assertThat(result).hasSize(2); } }
-
미해결Practical Testing: 실용적인 테스트 가이드
영속성 계층과 E2E 테스트에 대해 질문이 있습니다.
안녕하세요! 소프트웨어의 안정성과 테스트에 대해서 많은 고민을 가지고 있는 주니어 백엔드 개발자입니다.우빈님의 강의로 실용적인 테스팅 방식에 대해서 기반을 다졌습니다. 덕분에 면접 질문에서도 우빈님의 의견과 저의 의견이 합쳐져서 좋은 답변을 할 수 있었어요!취업 이후에도 여러가지 테스트 서적을 읽으면서 다른 관점도 많이 바라보고 있는데요. 이 과정에서 영속성 계층에서의 테스트와 E2E 테스트 부분에서 해소되지 않은 고민이 있어서 조언을 얻고자 찾아왔습니다.먼저 영속성 테스트 관련 질문부터 드리고자 합니다.취업을 하기 전에 취준을 하는 상황에서는 H2 DB로만 해소 할 수 있는 상황이 많았던 것 같습니다.하지만 실무에 들어오니 생각보다 Native Function을 사용하는 경우가 있고, 버전 문제로 각 DB에 있는 연산자가 제대로 동작하지 않는 일 (Dialect 이슈) 도 적지 않게 볼 수 있었습니다.이전에 답변 주신 https://www.inflearn.com/community/questions/1408867/classicist-vs-mockist해당 내용처럼 저 또한 동일하게 영속성 계층에서의 테스트를 DB 관점에서 그냥 동작하겠거니 하고 안일하게 생각하여 제대로 동작하지 않는 케이스를 봤기에 할 필요가 있다고 생각을 하는데요.아직까지는 제 경험에서는 Test Container를 사용하여 실 상황과 유사한 DB를 사용하는 것이 그나마 합리적인 방법으로 생각을 하고 있습니다. 하지만, 또 컨테이너가 뜨다보니 테스트하는데 걸리는 시간이 상당한 것도 단점으로 다가오기는 하더라구요.명확한 답은 없겠지만 우빈님은 위와 같은 비슷한 상황에서 어떤 방식을 택하고 계시는지 더 좋은 방법은 없을지하여 질문을 먼저 드리게 되었습니다.이어서, E2E 테스트 관련 질문입니다.비즈니스 계층에서의 통합 테스트가 작성이 되었다면 아직까지는 우빈님의 생각과 동일하게 프레젠테이션 영역에서는 통합 테스트를 하지 않아도 괜찮지 않을까? 라는 생각을 가지고 있는데요.하지만, 종종 테스트 관련 여러 서적이나 토론등을 보면 E2E 테스트는 중요하다는 관점을 가지고 있는 글이나 영상을 종종 찾아 볼 수 있었고, 무엇보다 저희 팀원분들중에서도 "E2E 테스트를 하는게 아니라면 프레젠테이션 레이어를 테스트 할 필요가 있어?" 라는 질문을 받았을 때 다음과 같은 생각이 들었습니다."그러네.. 지금까지는 거의 문서화를 목적으로 작성을 했었는데, 문서화를 RestDocs, RestDocs to Swagger(OpenApi Spec) 를 하는게 아니라면 작성 할 필요가 있을까?" 라는 의문이 떠올랐습니다. 혹시 이 부분에 대해서 어떻게 생각하시는지 우빈님의 소견을 듣고 싶어서 긴 글로 질문을 드리게 되었습니다.항상 좋은 강의 좋은 인사이트 제공 해주셔서 늘 감사드립니다.
-
미해결스프링 시큐리티 OAuth2
spring security 6부터 Implicit 지원 안하네요
spring security 6부터 AuthorizationGrantType.IMPLICIT가 제거되어서 OAuth2AuthorizationRequestException이 발생합니다.spring security 6로 실습 따라가면서 학습하시는 분들은 참고하세요https://github.com/spring-projects/spring-security/issues/11506
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
환경변수 이후 build failed
환경변수 까진 다시 설정했습니다만.. build 안되네요
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
정신이 나갈것같아요
ERROR: JAVA_HOME is set to an invalid directory: C:\Program Files\JavaPlease set the JAVA_HOME variable in your environment to match thelocation of your Java installation.C:\study\hello-spring>환경변수에 java-home 추가해서 경로 다시쓰고 자바 23버전 설치하고 인텔리제이 자바23 통일하고 다해봤는데. 안되네요
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
명령프롬프트 gradle builder 실행
경로 찾아서 gradle builder 하니 오류가 뜹니다.gradle clean builder 로 해도 오류가 뜨네요. 자바 경로를 찾을 수 없다고 뜨는 것 같은데...
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Model, model.addAttribute 생략
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]강의에서 @PostMapping("/converter/edit") public String converterEdit(@ModelAttribute Form form, Model model){ IpPort ipPort = form.getIpPort(); model.addAttribute(form); return "converter-form"; }와 같이 Model 을 사용하여 model.addAttribute(form)해주었는데 가독성적으로 추가한걸까요? 생략해도 동작하는걸로 알고있어서 만약 숙달되었다면 addAttribute해주는 코드를 생략해도되는지 아니면 다른이유가있는지 알고싶습니다.
-
미해결
spring-data-jpa 연습 중 순환참조 오류가 발생했습니다.
안녕하세요. 최근 spring-data-jpa 를 공부하다 이상한 순환 참조 오류를 발견했습니다.그런데 도통 납득이 되지 않아 여쭤보고자 질문 드립니다.spring-data-jpa 를 사용하기 전, 저는 주로 MyBatis 를 아래처럼 주로 사용했습니다.// 주로 mybatis interface 인 mapper 를 먼저 선언하고 @Mapper public interface MyBatisMapper {/* ... */} /* ------------ */ // DAO 객체에 주입해 사용하는 형태로 사용했습니다. @Repository public class MyBatisRepo { private final MyBatisMapper mapper; public MyBatisRepo(MyBatisMapper mapper) { this.mapper = mapper; } /* 생략 */ }그래서 JPA 에서도 이처럼 사용해 볼까 하는 마음에 연습하던 중, 순환참조 오류가 발생하였습니다. 아래는 spring-data-jpa 에서 오류가 발생한 코드입니다.TestEntity : 연습용 엔티티@Entity public class TestEntity { @Id private Long id; }DAO 인터페이스 : repository 규약public interface TestRepo { // 연습용이라 텅 비어있습니다. }JPA 인터페이스public interface JPATestRepo extends JpaRepository<TestEntity, Long> { // 연습용이라 텅 비어있습니다. }Repository 구현체@Repository public class JPATestRepoImpl implements TestRepo { private final JPATestRepo jpaRepo; public JPATestRepoImpl(JPATestRepo jpaRepo) { this.jpaRepo = jpaRepo; } // 연습용이라 이후 아무 내용도 없습니다. }실행시 발생하는 에러2024-10-19T19:46:00.760+09:00 WARN 66384 --- [testing] [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'JPATestRepoImpl' defined in file [/~~~/Desktop/Coding/testing/build/classes/java/main/core/testing/JPATestRepoImpl.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'JPATestRepoImpl': Requested bean is currently in creation: Is there an unresolvable circular reference? 2024-10-19T19:46:00.761+09:00 INFO 66384 --- [testing] [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2024-10-19T19:46:00.762+09:00 INFO 66384 --- [testing] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2024-10-19T19:46:00.807+09:00 INFO 66384 --- [testing] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 2024-10-19T19:46:00.811+09:00 INFO 66384 --- [testing] [ main] .s.b.a.l.ConditionEvaluationReportLogger : Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2024-10-19T19:46:00.825+09:00 ERROR 66384 --- [testing] [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌──->──┐ | JPATestRepoImpl defined in file [/~~~/Desktop/Coding/testing/build/classes/java/main/core/testing/JPATestRepoImpl.class] └──<-──┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true. Process finished with exit code 1 콘솔 에러에 따르면 JPATestRepoImpl 가 자기 자신을 의존해 순환참조가 발생한다고 합니다.하지만 JPATestRepoImpl 는 (코드에서 볼 수 있듯이) JPATestRepo 를 주입받을 뿐, 자기자신을 의존하고 있지 않습니다. 게다가 더 혼란스러운 점은 만약 JPATestRepoImpl 의 이름을 다른 것으로 바꾸면 (예를 들어 TestRepoJPAImpl) 거짓말처럼 순환 참조 오류가 없어집니다. 제가 추측하기로는 스프링이나 JPA 가 bean 이름을 헷갈려 발생하는 오류 같은데, 이를 헷갈려 하는 이유를 도통 모르겠습니다.당연히 컴퓨터 재시작, 프로젝트 clean, rebuild, 프로젝트 재생성해 시도해봤지만 모두 같은 현상이 나타납니다. 도대체 어떤 이유 때문에 이런 현상이 일어나는 걸까요...?Github repo : https://github.com/jbw9964/testing
-
미해결Practical Testing: 실용적인 테스트 가이드
builer 생성 방식 메서드 분리
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 강의를 보면 createProduct 를 통해서 builder 패턴 생성자 생성 방식을 공통메서드로 분리해주셨는데요. 이는 builder 패턴이 제공해주는 생성자 생성방식의 유연성을 함수로 분리함으로써 인해 제약을 주는 것이기도 한 거 같다는 생각이 들었습니다. 만약에 각 Product마다 필요한 컬럼이 다르다면 createProduct 함수로 분리하는게 아닌 반복이 되지만 어쩔 수 없이 각각 builder() + 체이닝 방식으로 코드를 기술하셨을지, 아니면 필요한 함수 시그니처마다 함수를 분리하셨을지 궁금합니다.
-
해결됨Spring framework 개발 환경 구축하기와 Dao 리팩토링하기
[해결 방법] Autowired가 적용이 안되는 문제 + context에서 NullPointerException
build.gradle 추가testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.3' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.3'
-
미해결스프링 시큐리티 OAuth2
인증을 세션이 아닌 jwt로 할 경우 질문입니다.
현재 oauth2Login api를 사용해서 사용자 로그인 인증에 성공하면 인증객체가 세션에 저장되고, 이후 인가서버에 code 요청시 OAuth2AuthorizationEndpointFilter 에서도 securityContext에 저장돼있는 인증객체로 인해 인증성공으로 판단후 code를 발급합니다. 근데 세션 기반이 아닌, jwt 토큰을 받아서 검증후 인증을 처리하는 로직이라면 jwt 토큰을 검증하는 필터인 resource Server api의 필터가 OAuth2AuthorizationEndpointFilter 보다 뒤에 있기 때문에 인증객체가 없어서 코드발급을 안해줄텐데 이럴땐 어떻게 해야하나요? jwt토큰을 검증하고 인증객체를 만드는 필터를 OAuth2AuthorizationEndpointFilter 앞에 만든다고 쳐도 그러면 resource Server api의 필터와 똑같이 동작하는 필터가 생기는건데 좋은 방법인것 같진 않습니다. 어떻게 해야 하나요?
-
미해결스프링 시큐리티 OAuth2
Resouce owner 인증 전 단계 질문입니다.
클라이언트가 인가서버에게 인가코드를 요청하기 전에 사용자인증을 받기 위해 사용자 id, 비밀번호를 입력하는 단계가 먼저 거치고, 인증이 되면 인가서버에게 /oauth2/authorize url로 response_type=code ~~ 이런식의 정보를 포함해서 요청하게 되는데 그러면 사용자가 인증받을때 입력한 사용자 id, 비밀번호를 통해 인증처리를 하는건 인가서버에서 하는게 아닌건가요? 인가서버에서 하는거라면 입력한 사용자 id, 비밀번호는 어떤 필터에서 처리하는건가요?
-
미해결Spring framework 개발 환경 구축하기와 Dao 리팩토링하기
[해결 방법] findOne 에러 / Inferred type 'S' for type parameter 'S' is not within its bound; should extend 'com. spring. www. domain. User'
@RunWith(SpringRunner.class) @SpringBootTest public class UserRepositoryTest { @Autowired UserRepository userRepository; @Test public void save() { //User user = new User("kms", "kms1234", "hongildong"); //userRepository.save(user); User user = new User(); user.setId("kms"); Example<User> userExample = Example.of(user); User selectedUser = userRepository.findOne(userExample).orElse(null); assertEquals("kms", selectedUser.getId()); } }
-
미해결Spring framework 개발 환경 구축하기와 Dao 리팩토링하기
[해결 방법] Java8 버전 사용하고 싶으신 분들은 아래와 같이 설정하시면 됩니다.
build.gradleplugins { id 'java' id 'org.springframework.boot' version '2.3.12.RELEASE' id 'io.spring.dependency-management' version '1.0.10.RELEASE' } group = 'com.spring.www' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'mysql:mysql-connector-java:8.0.32' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() }gradle-wrapper.propertiesdistributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
트랜잭션 전파 활용1,2 강의 내용 중 질문
강의록에 첨부해주신 그림을 보면 MemberRepository는 con1를 사용하고, LogRepository는 con2를 사용하는데 이게 각각 MemberRepository는 트랜잭션B를 사용하고 LogRepository는 트랜잭션C를 사용한다고 해서 다른 커넥션을 사용하게 되는거죠?? MemberRepository는 con1를 사용하고 커밋하고 난 후에 커넥션 풀에 con1이 반납되고 그 후에 LogRepository가 트랜잭션을 시작하게 되면 con1을 사용하게 되는건 아닌가요?? 이럴때는 서로가 다른 트랜잭션B,C로 구분이 안되어있을때만 이렇게 되는건가요..??
-
미해결스프링 시큐리티 완전 정복 [6.x 개정판]
세션클러스터링 적용후 중복로그인 체크
안녕하세요. 스프링 시큐리티 완전 정복 6.x 버전 잘 들었습니다.궁금한 점 이 있는데요.세션 클러스터링을 통해서 redis를 적용하면 세션이 tomcat -> rediss로 변경되는 부분 확인하였고, 잘 동작되는 것도 확인하였습니다.궁금한 점은 단일 서버일 때 중복 로그인 설정을 하면 session 체크를 ConcurrentSessionFilter에서 하는 것으로 알고 있는데, 세션 클러스터링을 적용하면 동시 세션을 어떻게 체크하는지 궁금하고, 이것을 제어하기 위해 설정이 추가로 필요한지 궁금합니다.단일 서버일 경우 sessionManagement를 통해서 maximumSessions(1) 인 것을 체크를 하게 되는데, 세션 클러스터링을 적용할 경우 이 값을 어떻게 공유하면서 처리하는지 궁금합니다.감사합니다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
ConversionService와 Formatter 역할
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용] 정리를 하자면 아래와 같은지 확인하고 싶습니다.ConversionServiceHTTP 요청 파라미터(문자) → 타입에 맞는 객체로 변환객체 → HTML 렌더링을 위한 문자로 변환Formatter숫자/날짜 → 지정된 형식의 문자열로 변환 (예: 10000 → "10,000")형식화된 문자열 → 객체로 변환 ("2024-10-19 14:05:57" → localDateTime 객체)
-
미해결Practical Testing: 실용적인 테스트 가이드
Classicist VS. Mockist
우빈님. 안녕하세요! 질문이 있어서 남겨드립니다.제가 테스트할때는 repository부분의 쿼리메서드나 jpql로 작성한 코드들은 따로 테스트를 하지 않고 QueryDSL같은 외부 라이브러리를 사용할때만 단위 테스트를 진행합니다. 그리고 비즈니스 레이어에 대해서는 위의 repository를 mocking하여 사용하고 컨트롤러 부분에서 통합테스트를 진행합니다.해당 부분에서 우빈님과 하는 방식이 다른것 같습니다. 우빈님은 비즈니스 레이어에서 통합테스트를 진행하고 Presentation 레이어에서 mocking을 이용한다고 하셨는데 혹시 제가 하는 방식에서 조언을 주실 수 있으실지 잘못된 방향성으로 가고 있는지에 대해 여쭤보고 싶습니다. 또한 우빈님께서 그렇게 진행하시는 이유에 대해 듣고 싶습니다.
-
미해결스프링 시큐리티 완전 정복 [6.x 개정판]
인증 컨텍스트 - SecurityContext / SecuriryContextHolder - 2
강의 13분 50초를 보면this.securityContextHolderStrateget.setContext(context);this.securityContextRepository.saveContext(context, request, response);두 메서드가 존재합니다.전자는 ThreadLocal에 인증객체를 저장한다라고 하셨고,후자는 세션에 저장한다라고 설명해주셨는데 개인적으로 잘 이해가 안갑니다. 차이가 무엇인지...
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
Test에서 오류
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 코드는 다음과 같이 작성했는데 다음과 같은 문제가 발생합니다. 코드를 여러번 수정하고 계속 확인하는데 고쳐지지가 않아서 질문드립니다![질문 내용]여기에 질문 내용을 남겨주세요.