묻고 답해요
130만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 핵심 원리 - 기본편
NoUniqueBeanDefinitionException 에러
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: expected single matching bean but found 2: rateDiscountPolicy,getDiscountPolicy이 에러가 나서 원인을 찾아보니 AppConfig의 @Bean public DiscountPolicy getDiscountPolicy() { //return new FixDiscountPolicy(); return new RateDiscountPolicy(); }이 부분과@Component public class RateDiscountPolicy implements DiscountPolicy {}이 부분때문에 RateDiscountPolicy 빈이 2개로 등록되어서 그런 것 같아 AppConfig에서 @Bean어노테이션을 주석처리하니 정상적으로 테스트가 됐는데요. 궁금한 점은 AutoAppConfig 에서 AppConfig에 대한 부분은 제외를 시켰는데 왜 중복이 되는가입니다....@Configuration @ComponentScan( basePackages = "hello.core.member", excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class) ) // 기존 AppConfig.java 때문에 컴포넌트 스캔 대상에서 제외시킴 public class AutoAppConfig {}
-
해결됨스프링 시큐리티 OAuth2
JdbcOAuth2AuthorizationService에서 DB에 저장시 오류 원인
OAuth2AuthorizationService 구현체를 JdbcOAuth2AuthorizationService로 변경하였습니다.Spring Authorization Server 버전은 1.0 입니다.@Bean public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); }DB Schema : Spring Authorization Server 에서 제공/* IMPORTANT: If using PostgreSQL, update ALL columns defined with 'blob' to 'text', as PostgreSQL does not support the 'blob' data type. */ CREATE TABLE oauth2_authorization ( id varchar(100) NOT NULL, registered_client_id varchar(100) NOT NULL, principal_name varchar(200) NOT NULL, authorization_grant_type varchar(100) NOT NULL, authorized_scopes varchar(1000) DEFAULT NULL, attributes blob DEFAULT NULL, state varchar(500) DEFAULT NULL, authorization_code_value blob DEFAULT NULL, authorization_code_issued_at timestamp DEFAULT NULL, authorization_code_expires_at timestamp DEFAULT NULL, authorization_code_metadata blob DEFAULT NULL, access_token_value blob DEFAULT NULL, access_token_issued_at timestamp DEFAULT NULL, access_token_expires_at timestamp DEFAULT NULL, access_token_metadata blob DEFAULT NULL, access_token_type varchar(100) DEFAULT NULL, access_token_scopes varchar(1000) DEFAULT NULL, oidc_id_token_value blob DEFAULT NULL, oidc_id_token_issued_at timestamp DEFAULT NULL, oidc_id_token_expires_at timestamp DEFAULT NULL, oidc_id_token_metadata blob DEFAULT NULL, refresh_token_value blob DEFAULT NULL, refresh_token_issued_at timestamp DEFAULT NULL, refresh_token_expires_at timestamp DEFAULT NULL, refresh_token_metadata blob DEFAULT NULL, PRIMARY KEY (id) ); authorization code 발급시 로그인 화면 계정 정보 입력 후 MariaDB에 저장할떄 .BadSqlGrammarException 예외가 발생합니다, 질문1)에러 로그를 보면 insertAuthorization 메소드에서 attribute 컬럼에 Principal Class 자체를 저장하는 과정에서 발생하고 있습니다,다만,퀴리를 DB에서 직접 실행하면 저장이 되는것 으로 보아 Spring JDBCTemplate에서 변환시 오루가 발생하는거 같은데 원인이 궁급합니다. private void insertAuthorization(OAuth2Authorization authorization) { List<SqlParameterValue> parameters = this.authorizationParametersMapper.apply(authorization); try (LobCreator lobCreator = this.lobHandler.getLobCreator()) { PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator, parameters.toArray()); this.jdbcOperations.update(SAVE_AUTHORIZATION_SQL, pss); } } Caused by: java.sql.SQLSyntaxErrorException: (conn=952251) Could not convert [{"@class":"java.util.Collections$UnmodifiableMap","java.security.Principal":{"@class":"org.springframework.security.authentication.UsernamePasswordAuthenticationToken","authorities":["java.util.Collections$UnmodifiableRandomAccessList",[{"@class":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"ROLE_USER"}]],"details":{"@class":"org.springframework.security.web.authentication.WebAuthenticationDetails","remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"B8C2C171954B719820D6592F6102AF9B"},"authenticated":true,"principal":{"@class":"org.springframework.security.core.userdetails.User","password":null,"username":"user1","authorities":["java.util.Collections$UnmodifiableSet",[{"@class":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"ROLE_USER"}]],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null},"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest":{"@class":"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest","authorizationUri":"http://localhost:9000/oauth2/authorize","authorizationGrantType":{"value":"authorization_code"},"responseType":{"value":"code"},"clientId":"messaging-client","redirectUri":"http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc","scopes":["java.util.Collections$UnmodifiableSet",["openid"]],"state":null,"additionalParameters":{"@class":"java.util.Collections$UnmodifiableMap","grant_type":"authorization_code"},"authorizationRequestUri":"http://localhost:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&grant_type=authorization_code","attributes":{"@class":"java.util.Collections$UnmodifiableMap"}}}] to -4 RegisteredClientRepository는 구현체를 JdbcRegisteredClientRepository 로 변경해도 이상없이 동작 하고 있고 JdbcAuthorizationConsentService는 JdbcOAuth2AuthorizationService 오류로 동의 단게가 진행되지 않아서 확인해보지 않았습니다. 질문2)이외에도Authorization Server에서 blob 컴럼에 Class 자체를 저장하는 사례가 많이 있는데 커스참 구현체를 만들어서 Class 를 저장하지 않고 Authorization Server 필요한 항목만 JSON 으로 저장 하거나 테이블을 정규화 하여 Map 에 담아 주는것이 좋은 방법일까요? 참고로 Attribe는 JwtGenerator에서 보면 nonce 을 가지고 오고 있습니다.if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType())) { OAuth2AuthorizationRequest authorizationRequest = context.getAuthorization().getAttribute( OAuth2AuthorizationRequest.class.getName()); String nonce = (String) authorizationRequest.getAdditionalParameters().get(OidcParameterNames.NONCE); if (StringUtils.hasText(nonce)) { claimsBuilder.claim(IdTokenClaimNames.NONCE, nonce); } } 에러로그 전문org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [INSERT INTO oauth2_authorization (id, registered_client_id, principal_name, authorization_grant_type, authorized_scopes, attributes, state, authorization_code_value, authorization_code_issued_at, authorization_code_expires_at,authorization_code_metadata,access_token_value,access_token_issued_at,access_token_expires_at,access_token_metadata,access_token_type,access_token_scopes,oidc_id_token_value,oidc_id_token_issued_at,oidc_id_token_expires_at,oidc_id_token_metadata,refresh_token_value,refresh_token_issued_at,refresh_token_expires_at,refresh_token_metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:101) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1538) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:667) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:960) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1015) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService.insertAuthorization(JdbcOAuth2AuthorizationService.java:211) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService.save(JdbcOAuth2AuthorizationService.java:189) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider.authenticate(OAuth2AuthorizationCodeRequestAuthenticationProvider.java:209) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-6.0.1.jar:6.0.1]at org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter.doFilterInternal(OAuth2AuthorizationEndpointFilter.java:166) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter.doFilterInternal(OAuth2AuthorizationServerMetadataEndpointFilter.java:84) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:116) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.AuthorizationServerContextFilter.doFilterInternal(AuthorizationServerContextFilter.java:61) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.0.1.jar:6.0.1]at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-6.0.4.jar:6.0.4]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.0.4.jar:6.0.4]at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.4.jar:6.0.4]at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.5.jar:10.1.5]at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]Caused by: java.sql.SQLSyntaxErrorException: (conn=952251) Could not convert [{"@class":"java.util.Collections$UnmodifiableMap","java.security.Principal":{"@class":"org.springframework.security.authentication.UsernamePasswordAuthenticationToken","authorities":["java.util.Collections$UnmodifiableRandomAccessList",[{"@class":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"ROLE_USER"}]],"details":{"@class":"org.springframework.security.web.authentication.WebAuthenticationDetails","remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"B8C2C171954B719820D6592F6102AF9B"},"authenticated":true,"principal":{"@class":"org.springframework.security.core.userdetails.User","password":null,"username":"user1","authorities":["java.util.Collections$UnmodifiableSet",[{"@class":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"ROLE_USER"}]],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null},"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest":{"@class":"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest","authorizationUri":"http://localhost:9000/oauth2/authorize","authorizationGrantType":{"value":"authorization_code"},"responseType":{"value":"code"},"clientId":"messaging-client","redirectUri":"http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc","scopes":["java.util.Collections$UnmodifiableSet",["openid"]],"state":null,"additionalParameters":{"@class":"java.util.Collections$UnmodifiableMap","grant_type":"authorization_code"},"authorizationRequestUri":"http://localhost:9000/oauth2/authorize?response_type=code&client_id=messaging-client&scope=openid&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc&grant_type=authorization_code","attributes":{"@class":"java.util.Collections$UnmodifiableMap"}}}] to -4at org.mariadb.jdbc.export.ExceptionFactory.createException(ExceptionFactory.java:282) ~[mariadb-java-client-3.0.10.jar:na]at org.mariadb.jdbc.export.ExceptionFactory.create(ExceptionFactory.java:336) ~[mariadb-java-client-3.0.10.jar:na]at org.mariadb.jdbc.BasePreparedStatement.setInternalObject(BasePreparedStatement.java:1172) ~[mariadb-java-client-3.0.10.jar:na]at org.mariadb.jdbc.BasePreparedStatement.setObject(BasePreparedStatement.java:608) ~[mariadb-java-client-3.0.10.jar:na]at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setObject(HikariProxyPreparedStatement.java) ~[HikariCP-5.0.1.jar:na]at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:415) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:236) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:152) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.ArgumentPreparedStatementSetter.doSetValue(ArgumentPreparedStatementSetter.java:65) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService$LobCreatorArgumentPreparedStatementSetter.doSetValue(JdbcOAuth2AuthorizationService.java:621) ~[spring-security-oauth2-authorization-server-1.0.0.jar:1.0.0]at org.springframework.jdbc.core.ArgumentPreparedStatementSetter.setValues(ArgumentPreparedStatementSetter.java:50) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.JdbcTemplate.lambda$update$2(JdbcTemplate.java:963) ~[spring-jdbc-6.0.4.jar:6.0.4]at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651) ~[spring-jdbc-6.0.4.jar:6.0.4]... 68 common frames omitted
-
미해결스프링 핵심 원리 - 기본편
Repository는 싱글톤 상태인가요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]AppConfig 리팩토링 강의에서 질문드립니다!DIP를 지켜주기 위해 AppConfig의 MemberRepository()를 inject하는 과정에서,memberRepository() 메소드가 MemoryMemberRepository() 객체를 생성해서 넘겨주게 되는데,이 경우에, OrderService로 넘어가는 memberRepository와 MemberService로 넘어가는 memberRepository 객체가 동일한 객체인가요??public MemberService memberService() { return new MemberServiceImpl(memberRepository()); } private static MemoryMemberRepository memberRepository() { return new MemoryMemberRepository(); } public OrderService orderService() { return new OrderServiceImpl(memberRepository(), discountPolicy()); } 제가 생각한 바로는, MemberService, OrderService 각각에 들어가는 객체가 new로 들어가, 다른 repository를 참조하는 것 같아서요! 예시를 위해 이렇게 구현하신 건지.. 아니면 실제 repository가 싱글톤으로 돌아가는지 궁금합니다..!!싱글톤으로 돌아간다 하더라도, 어떻게 각각에 new를 해줬는데 싱글톤일 수 있는지도 궁금하네요..!! 감사합니다.
-
해결됨자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
id 객체에 추가할 때 null 을 넣어주신 이유가 궁금합니다!
안녕하세요. 질문이 있습니다,@Id@GeneratedValue(...)private Long id; 이렇게는 사용해봤는데 처음에 =null 을 사용하신 이유가 궁금합니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
HTTP 응답 데이터 - json
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.response.setContentType을 application/json로 지정하면 utf-8을 사용하도록 정의되어 있다고 하셨는데 utf-8을 제외하고 실행하면 한글이 깨집니다.
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
강제 지연 로딩 초기화에 관한 질문입니다.
우선 2개의 질문을 참고하였습니다.이것과이것입니다. 두개의 글을 종합해 보면, V1에서 Order리스트를 반환할때는 Lazy방식이기 때문에 Member객체가 프록시 객체이고, Jackson이 해당 해당 객체를 접근하는 순간 초기화한다는 것입니다. 그리고 해당 프록시 객체를 JSON으로 읽을수 있도록 도와주는 것이 하이버네이트5모듈이라는 것입니다. 여기서 궁금한 점이//강제 지연 로딩 설정 hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true);이 코드입니다. 해당 코드는 강제 지연 로딩 설정이라고 하는데강의 21분 20초경 LAZY를 강제 초기화 한다고 나와있습니다.저는 위의 코드와 for (Order order : orders) { order.getMember().getName(); order.getDelivery().getAddress(); }이 코드가 같은 역할을 한다고 이해했습니다. 위의 두개의 코드를 삭제하면, 포스트맨으로 요청을 보냈을 때,Member, orderItems, Delivery에는 null 값이 들어갑니다. 두개의 코드중 하나를 넣으면 요청을 넣었을때 Member, orderItems, Delivery에 실제 값이 들어가게 됩니다. 하지만 위의 두 글을 종합한 내용을 보면Jackson이 해당 해당 객체를 접근하는 순간 초기화라는 내용이 있습니다. 그렇다면 이미 초기화 된 프록시 객체일텐데 왜 위의 강제초기화 코드를 넣어야 실제 값이 들어가게 되는지 궁금합니다. jackson이 프록시 객체에 접근할때 초기화가 되어서 두 코드를 넣지 않아도 null이 아닌 실제 값이 나와야하는 것 아닌가요? 글이 너무 길어 죄송합니다..ㅠㅠ
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
@RestController, @GetMapping import가 안됩니다. ㅠ
안녕하세요. 선생님따라서 코딩중인데 구글링결과대로 시도해봐도 딱히 해결되지 않아서 질문 남겼습니다... 제목 그대로 import 자체가 되지 않아요 ㅠ
-
미해결스프링 DB 1편 - 데이터 접근 핵심 원리
꼭 close()를 해줘야 하나요??
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]어디서 들은건데 java6부터인가 Connection, PreparedStatement, ResultSet 클래스가 상속받은 클래스중 AutoCloseable이 있어서 close로 받아 주지 않아도 상관없다는 얘기를 들었는데 잘못된 점이 있으면 말씀해주시면 감사하겠습니다.
-
미해결자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
@Repository 빨간줄
BookMemoryRepository랑 BookMysqlRepository에 @Repository를 모두 붙이면 빨간줄이 강의상에선 나는데 저는 이상이 없는 데 혹시 스프링 버전 차이인건지 궁금합니다.
-
미해결토비의 스프링 부트 - 이해와 원리
ApplicationContext.refresh() 하는 이유
registerBean 이후 refresh를 하는 이유가 궁금합니다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
if (loginMember == null)의 존재 이유
HomeController.java @GetMapping("/") public String homeLoginV3(HttpServletRequest request, Model model) { HttpSession session = request.getSession(false); //세션 저장소에 해당 세션이 존재하지 않음 if (session == null) { return "home"; } //세션 저장소에 해당 세션이 존재함 Member loginMember = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER); if (loginMember == null) { return "home"; } model.addAttribute("member", loginMember); return "loginHome"; }위 코드에서 'if (loginMember == null) { ... }' 코드가 존재하는 이유는, loginMember가 null인 경우는 없지만 session.getAttribute()가 스펙상 null을 반환할 수 있기 때문인 것이 맞을까요..?세션 저장소에 세션이 없는 경우는 있지만, 세션이 있다고 하면 Member 객체가 무조건 존재하는 것이 아닌가요..??
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
빈 ModelAndView 반환
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]여기에 질문 내용을 남겨주세요.수업중에 return new ModelAndView();로 반환시 정상흐름으로 서블릿이 리턴된다고 하셨는데 그렇다는 건 response의 값이 WAS로 가서 처리된다는 것인가요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
쿠키와 세션
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/)[질문 내용]여기에 질문 내용을 남겨주세요.HttpSession 사용하기전에는 String sessionId = UUID.randomUUID().toString(); sessionStore.put(sessionId,value); Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId); response.addCookie(mySessionCookie); 세션과 쿠키를 직접 만들어주셨는데login3 부터는HttpSession session = request.getSession(); session.setAttribute(SessionConst.LOGIN_MEMBER,loginMember);세션만 만들어주셨는데 쿠키는 저희가 직접 만들어줬던걸 이제 자동으로 해주는건가요 ?? 질문2 세션은 웹 브라우저를 종료하면 자동으로 삭제 되는거 아닌가요 ??
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
index.html 하이퍼링크 이동 시 basic.html로 이동
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]localhost:8080으로 접속하여 index.html로 이동하였습니다. 그 이후 jsp 회원가입 전송 후 /jsp/members/save에서 /index.html 로 이동하도록 되어있길래 그리했더니 같은 디렉토리의 basic.html로 이동하게 됩니다. 이동 경로를 '/'로 바꿔주어 해결하였으나 원인이 무엇인지 궁금합니다
-
해결됨토비의 스프링 부트 - 이해와 원리
코틀린으로 강의 따라하시는 분들이 계시다면 참고하세요. no-arg 설정이 필요합니다.
BeanPostProcessor는 매개변수가 없는 no-arg 생성자를 필요로 하는데요, 자바의 경우 굳이 매개변수를 생성자에서 주입하지 않아도 되지만 코틀린의 경우 JPA 엔티티나 현재 강의의 ServerProperties처럼 프로퍼티값을 읽어들여 객체를 생성하는 경우 생성자 파라미터를 보통 사용하실 텐데요,이 경우 따로. noarg 생성자가 만들어지지 않기 때문에 BeanPostProcessor가 디폴트 생성자를 만들어낼 수 있도록kotlin("plugin.allOpen") kotlin("plugin.noArg")을 이용해서 @Component 어노테이션으로 빈에 대한 no-arg 기능을 활성화해야합니다.gradle에서 다음과 같이 plugin을 포함하시구요plugins{ kotlin("plugin.noarg") version "1.7.22" //jpa를 사용하신다면 kotlin("plugin.jpa")에 포함되어 있습니다. } 아래에 Component 어노테이션에서 no-arg가 활성화될 수 있게 해당 부분을 적어주시면 됩니다.noArg { annotation("org.springframework.stereotype.Component") }all-open, no-arg 등의 플러그인에대한 더 자세한 설정에 대해서 궁금하시다면 아래 글을 참고하시면 좋습니다.https://techblog.woowahan.com/2675/
-
미해결토비의 스프링 부트 - 이해와 원리
스프링의 장점
안녕하세요. 토비님 강의 너무 잘듣고 있습니다. 강의를 듣다가 궁금한점이 생겨서 질문남깁니다.우선 저는 독립형 스프링 애플리케이션까지 들었습니다. 강의를 들으면서 스프링과 스프링 부트의 차이점과 이점은 이해가 쉽게 됐습니다. 하지만 서블릿 웹서버와 스프링 웹서버의 차이점에 대해서는 명확하게 이해하지 못했습니다.독립 실행형 서블릿 애플리케이션으로 웹 서버를 띄우는 것보다 독립 실행형 스프링 애플리케이션으로 웹 서버를 구성하는 것이 어떤 장점이 있는지 궁금합니다. 제가 이해한 바로는 서블릿 웹서버는 HTTP요청이 들어오면 컨테이너가 서블릿을 Mapping하고 서블릿이 요청을 처리하는 것이고 스프링 웹서버는 서블릿에서 스프링 컨테이너를 이용해서 요청을 처리하는 것입니다.혹여나 개념을 잘못 이해하고 있거나 뒷 내용에 이것에 대한 내용이 나온다면 알려주시면 감사하겠습니다.
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
단일 체크박스 - 체크해제 설정
강의 내용 중 단일 체크박스를 체크하면 원하는 상태로 변하는데, 체크를 풀면 500에러가 발생합니다. 어떻게 코드를 수정해야 할 지 도움을 요청드립니다.강의 내용을 조금 변형해서 - '할일'을 등록할 때 할일 완료상태면 TODO_COMPLETE, 미완료이면 TODO_INCOMPLETE이 되도록 아래와 같이 작성하였습니다. (Todo.java)1) 등록된 할일을 수정할 때 단일 체크박스를 체크하면 완료, 체크를 풀면 미완료가 되도록 아래와 같이 editTodo.html을 작성했습니다.이 경우, 할일 수정 페이지에서 체크를 할 때는 TODO_COMPLETE으로 상태가 잘 변경됩니다. 그런데 다시 체크를 풀면 500에러가 발생하는데요. (org.thymeleaf.exceptions.TemplateInputException 입니다) 그래서 Controller에서 할일 수정하는 핸들러의 구현부에 디버거를 걸고 확인을 했더니빨간 네모에서 처럼 체크를 풀고 저장버튼을 누르면 TODO_INCOMPLETE로 변경되는 것이 아니라 null로 넘어가고 있습니다.2) 그래서 강의 복습 및 구글링으로 아래와 같이 체크박스 부분을 수정했는데요.그랬더니 todoCmplt 상태는 무조건 TODO_INCOMPLETE로 고정되어 체크박스에 체크를 하고 할일 수정(저장)을 해도 체크는 풀리게 되었습니다. (editTodo.html의 체크박스를 여러 번 다르게 수정했지만 모두 실패했습니다.)Todo.java에서 코드가 잘못된 것인지, editTodo.html이 잘못된 것인지 알 수 없어 문의드립니다.
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
region.key 와 region.value 가 어디서 나오는 것인가요? 그리고 region 의 타입은 무엇이 되는건가요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)네3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)네[질문 내용]여기에 질문 내용을 남겨주세요.@ModelAttribute("regions") public Map<String, String> regions() { Map<String, String> regions = new LinkedHashMap<>(); regions.put("SEOUL", "서울"); regions.put("BUSAN", "부산"); regions.put("JEJU", "제주"); return regions; }<div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <input type="checkbox" th:field="${item.regions}" th:value="${region.key}" class="form-check-input" disabled> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> </div> </div> 여기에서 th:each="region : ${regions}"th:value="${region.key}"th:text="${region.value}" 너무 어려워서 체크박스만 어제 오늘 계속 붙들고 다시보고 있는데요 이제 저 3개만 알면 이해갈거같은데 전력을 다해 검색해봐도 도저히 저 3개를 모르겠어요each 에 ${regions} 는 맵인거 같은데 region 은 무슨타입이 들어가길래 key value 라는 값을 쓸수 있는건가요?
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
회원 도메인과 리포지토리 만들기 강의를 듣는중인데요
따라치기에 바쁘고 코드를 적으시는데 이해못하면 잘못 듣고있는건가요? 자바 강의는 다 들었는데 뭘하시는지를 모르겠고 따라적기만 하고있습니다..
-
미해결토비의 스프링 부트 - 이해와 원리
HikariDataSource가 존재하지 않는 이슈가 있습니다.
토비님이랑 같은 의존성 주입 받았는데 전 HikariDataSource라는 class가 존재하지 않습니다. 제가 잘못한게 있을까요