묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링 시큐리티
스프링부트 버전 2.7이상 authenticationManagerBean 등록방법을 잘 모르겠습니다.
안녕하세요 선생님 일단 좋은 강의 감사합니다.선생님 강의를 들으면서 2.7버전으로 진행하고 있는데매니저 를 등록할때부터 막혔습니다. 제 나름대로 소스도 좀 제가 평소에 하던 방식으로 하고있었는데 강의 버전이랑 맞지 않으니 힘드네요 ㅠ 이 부분은 스프링부트 2.7이상에서는 어떻게 하면될까요? 깃허브 주소 : https://github.com/PeachCoolPiece/corespringsecurity 입니다.
-
해결됨실전! Querydsl
init 메서드에 @Transactional
init() 메서드에 @Transactional 을 넣어주는 이유는 무엇인가요? 감사합니다.
-
해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
One-to-Many Self Referencing 문제
안녕하세요 One-to-Many 관계 정의 시에 해결이 잘 안되서 문의 드립니다. Typeorm 사이트에서 정보를 참조도 해보고 구글링도 해봐서 시도해봤지만 명확하게 원하던 결과가 나오지 않아서 문의드립니다.@Entity("Parts", { schema: "workplaces" }) export class Parts { @PrimaryColumn('varchar', { name: "partsNumber", unique: true, nullable: false }) partsNumber: string; @Column('varchar', { name: 'partsName', length: 500, nullable: true }) name: string; @Column('bool', { name: 'isMain', nullable: false, default: false }) isMain: boolean; @CreateDateColumn() createDate: Date; @UpdateDateColumn() updateDate: Date; @DeleteDateColumn() deleteDate: Date | null; // 여기부터가 문의 드리는 One to Many 관계설정 코드입니다. @ManyToOne(() => Parts, (parts) => parts.alternative, { onDelete: "SET NULL", onUpdate: "CASCADE" }) parts: Parts; @OneToMany(() => Parts, (alternative) => alternative.parts) @JoinColumn([{ name: "alternative", referencedColumnName: "partsNumber" }]) alternative: Parts[]; }보시면 아시겠지만, Parts 테이블에 alternative란 항목의 다수의 Parts 타입을 데이터로 받도록 구성한 것인데요, 부품의 경우, 대체 부품이 여럿이 존재해서 isMain 항목을 통해서 부품이 main부품일 경우에만 데이터를 저장하고 호출해주며, main 부품이 아닌경우, 연결된 main parts 의 정보를 호출해주도록 하기 위해서 입니다. 실제로 이렇게 구성할 경우, 아래와 같은 데이터를 저장할시{ "partsNumber": "341241", "name": "341241 Dryer Drum Belt", "isMain": true, "alternative": [ { "partsNumber": "14210003" }, { "partsNumber": "20065315" }, ] }이상없이 저장이 완료됩니다.Main part(parent) 검색시에 보통 사용하던 One to Many에서처럼 alternative에 제대로 2개의 배열객체가 출력이 됩니다만, 문제는 alternative 데이터를 검색하여 main이 되는 part를 출력하지 못하는 점인데요. Self Referencing 으로 One to Many관계를 설정시에 children측에서 parent 쪽의 정보를 호출하려면 어떻게 설정해야 하나요?항상 답변을 주셔서 감사합니다!
-
미해결스프링 핵심 원리 - 기본편
"역할과 구현의 분리" 이 한마디에 OOP 설계의 우주가 열린 기분이네요.
겨우 섹션1만 봤을 뿐인데 뭔가 설계에 있어서 북극성을 본 느낌. 왜 코드 몇줄보다 철학을 먼저 다루었는지 알것같다.controller,클래스 등(사용자) - 서비스 (자동차회사) - 인터페이스 들(자동차표준협회) - 구현체(자동차 부품업체 들)인터페이스1 (악셀,브레이크, 변속기, 오디오 등) - 업체 A -> 업체 B인터페이스 2( 자율주행, 카엔터테인먼트) - ...인터페이스 3(썬파노라마, 자동잠금장치 등등 ) - ...단일상속, 다중상속 서비스(자동차회사)
-
해결됨실전! Querydsl
valid 체크 메서드
안녕하세요인증할 때 != null 이라던가 StringUtils.hasText() 를 사용하시던데ObjectUtils.isEmpty() 함수를 사용하면 한번에 다 처리가 가능한데 사용안하는 이유가 있을까요? 다른 강의에서도 못 본 것 같아서요. 감사합니다.
-
미해결피그마(Figma)를 활용한 UI디자인 입문부터 실전까지 A to Z
프레임 질문 입니다.
프레임상에 그리드를 두고 디자인을하면반응형 웹디자인으로서핀터레스트 웹사이트 예시 처럼 웹사이트의 크기를 줄여나가면 컨텐츠들이그 웹사이트 조정한 크기에 맞게 변하는것 처럼.그리드 상에 디자인을 하면 화면 픽셀 단위 에 맞게 컨텐츠들이 조정된다고 이해를 하였습니다.모바일 반응형 디자인을 한다고 가정했을때모바일용 프레임 비율 한가지에다가 그리드 위에 디자인을 하면모바일 모든 디바이스에 디자인이 각 디바이스 해상도에 맞게 조정 되는것으로 이해를 했습니다.그렇다면피그마 상에서 위 사진처럼 모바일 디바이스별 프레임을 분류를 해놓는데제가 사진 위에 설명처럼 이해를 한다면,아이폰8 디바이스 프레임을 선택해서 그리드위에 디자인을 한다고 가정했을때그 디자인한 앱을아이폰11, 또는 아이폰12 등등 을 사용해도똑같은 디자인으로 결과값이 도출되는지가 궁금합니다.또한 아이폰 8 디바이스 프레임을 선택해서 그리드 위에 디자인한 앱과아이폰 11 디바이스 프레임을 선택해서 그리드 위에 디자인한 앱과똑같은 결과값이 나오는건가요?즉 그리드 를 사용해서 디자인을 한다면모바일 프레임이라면 어떤 프레임을 사용해도 상관이 없는건가요?또한그리드상 위에 디자인을하면 반응형으로서 각 디바이스 해상도에 맞게 조정이 되는것이라면왜 모바일용 해상도 프레임 or PC용 해상도 프레임 이런식으로 통일하지않고위 사진처럼 각 디바이스별로 프레임 해상도를 분류해놓는것인가요?모바일 프레임이라면 어떤 프레임을 선택해도 모든 모바일 디바이스상에 결과값이 동일한가요?질문이 많아서 불편드려 죄송합니다!위의 모든 질문은 디자인을 할시에어떤 프레임 상에 디자인을 해야되는지 궁금증에서 나왔으며어떤 프레임상에 디자인을 해도 결과값이 모두 동일한지 궁금증이 해결되지않습니다!저 많은 디바이스별 프레임중에 어떤 프레임에다가 디자인을 해야되는건가요?PC용 디자인을 하더라도 디자이너 분마다 디자인을 하게되는 프레임의 해상도 비율값이전부 다르시고 추천하시는 프레임 해상도 비율값도 전부 다르신데이 프레임의 해상도 비율값이 달라도 그리드 상에 디자인을 하면모두 동일한 디자인값이 나오는지가 궁금합니다!
-
미해결피그마(Figma)를 활용한 UI디자인 입문부터 실전까지 A to Z
웹 또는 앱 디자인 따라할때 질문.
실제 웹 따라할때 실제 픽셀 과 오브젝트 사이의 픽셀크기들 , 글꼴의 크기 어떻게 아는건가요??따라하려는 사이트 화면 확대축소 비율 100%로 캡처한후에피그마로 사진 가져온후 피그마 상의 확대비율 100%로 맞추면동일한 픽셀인것인가요? 추가로 만약 따라해서 제작한후에 피그마상에서 실제 사이트처럼 구현되는것을 확인할수있는 방법이 있을까요?
-
미해결피그마(Figma)를 활용한 UI디자인 입문부터 실전까지 A to Z
디자인 시스템 버튼 상태 질문입니다.
디자인 시스템 contenent 설명해주시는도중에버튼의 상태인normal / hover / press / focus / disabled 상태로 나타내셨고 text Field 상태를blank / filled / activated / error 로 표현하셨는데요 normal , press, disabled 등 몇가지는 어느정도 알겠는데hover focus activated 등등 무슨 상태를 표현하는 단어인건지 사전을 찾아봐도 정확히 감이오질않습니다. normal / hover / press / focus / disabled blank / filled / activated / error이상태들이 정확히 무슨상태인지 여쭈어 봐도될까요?
-
미해결피그마(Figma)를 활용한 UI디자인 입문부터 실전까지 A to Z
아이콘 제작 질문드립니다.
오브젝트의 라운딩값을 전체에 주는게 아닌일부만 라운딩 안줄수 있는 방법이 있을까요?
-
미해결[초급] 맛보자! 코틀린과 스프링으로 API 호출하기
BlogDto.decompiled.java 파일 보는법
BlogDto.decompiled.java 파일은 어디서 열 수 있나요?bulid 폴더에 들어가서 보려고하니까 /* compiled code */주석이 나오면서 보이지가 않습니다.
-
해결됨10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트
1940 주몽 문제 메모리 초과 질문있습니다!
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. https://www.acmicpc.net/source/share/f81085fda0c842898c7f440cecb12d4e 안녕하세요. 선생님 제가 선생님 풀이를 보기전에는 이렇게 2차원 배열을 통해서 구간별 누적합을 저장했는데 메모리 초과가 나와서 의문이였습니다. 무엇이 문제였을까요
-
미해결만들면서 배우는 리액트 : 기초
html 코드 오류
안녕하세요 강의 초반부에script 태그 3개를 추가했는데도 catItem내에서 html 코드를 쓰면 오류가 뜨네요이유가 뭘까요 ㅠㅠUncaught SyntaxError: Inline Babel script: Unexpected token (5:6) (at babel.min.js:7:10099) 3 | 4 | const catItem = { > 5 | < li > | ^ 6 | < img src="https://cataas.com/cat/HSENVDU4ZMqy7KQ0/says/react" /> 7 | </li > 8 | }<body> 아래는 제가 작성한 코드입니다. 주석 부분은 지웠습니다. <body> <div id="app"></div> <!-- <h1>1번째 고양이 가라사대</h1> <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> console.log("야옹"); const catItem = { <li> < img src="https://cataas.com/cat/HSENVDU4ZMqy7KQ0/says/react" /> </li> } const 여기다가그려 = document.querySelector("#app"); ReactDOM.createRoot(여기다가그려).render(catItem); </script> </body>
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버
디스어셈블리가 어디에 있는지 모르겠습니다 .
안녕하세요 수강생입니다. 강의를 듣다가 디스어셈블리를 찾고 있는데 없습니다. 강사님 VS에는 있는데 제가 쓰고 있는 VS에서는 없네요 어떻게 하면 될까요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Argument Resolver를 이용한 세션객체 활용성
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]실무에서 Argument Resolver를 이용해서 Session관련 객체를 컨트롤러의 파라미터로 받는 방법을 많이 이용하는 편인가요? 대부분 들어가는 기능인지, 있으면 좋고 없으면 말고 정도의 기능인지 궁금합니다.
-
미해결파이썬 무료 강의 (기본편) - 6시간 뒤면 나도 개발자
return과 print
return을 되도록이면 꼭 써주는 게 좋다고 질문 댓글에서 본 것 같은데 print를 쓰는 경우에는 return을 사용하지 않아도 되는 것인가요?둘 중에 하나만 작성하면 되는걸까요?
-
해결됨자바 기초부터 마스터하기 with 은종쌤 (Do it 자바 프로그래밍 입문) - Part 2(마스터편)
iterator 관련 질문입니다.
안녕하세요.iterator관련 해서 간단하게 질문 드립니다.hasNext() 와 next() 함수 모두 다음에 있는 요소에 관한 함수지 않습니까?코드를 작성하다가 문득 든 생각인데 이터레이터가 위치한 인덱스의 다음 인덱스를 뜻한다면 첫번째 원소는 어떻게 다룰 수 있는걸까?라는 의문이 들어서 질문글을 작성하게 되었습니다.1)여기서 다음의 뜻이 이터레이터가 위치한 인덱스를 말하는것인가요?아니면 이터레이터가 위치한 인덱스의 다음 인덱스를 말하는것인가요? 물론 전자여야 모든 뜻이 말이 되고 이해가 가기 때문에 전자겠지만자바 사이트에서 함수 정의를 보면 next라고 적혀 있어서 혹시나 해서 질문드립니다.전자가 맞다면 왜 하필 햇갈리게 next라고 했을까요? 2)그리고 hasNext가 이후에 요소가 있는지를 체크하는 함수라면 이터레이터가 arrayList의 마지막 인덱스에 위치할땐 false값을 리턴해서 마지막번째 원소를 다룰 수 없게 되지 않나요?혹시 arrayList도 마지막 원소에 c의 문자열 처럼 마지막에 null값이 항상 있기 때문에 마지막 원소까지 hasNext함수가 다룰 수 있는건지 궁금합니다. 다음이라는 단어 때문에 간단하던것들이 갑자기 모두 햇갈리네요.
-
미해결파이썬 무료 강의 (기본편) - 6시간 뒤면 나도 개발자
return
withdraw_night 함수에서 return 값을 commission과 balance - money - commision 두 가지로 줬는데 그러면 이 함수에서는 commission과 balance - money - commission 이 두 가지를 항상 같이 반환해야 하는 것인가요?
-
미해결스프링부트 시큐리티 & JWT 강의
소셜 로그인 후 JWT 발급
아무리 해도 해결이 안되네요...package com.example.project1.config.oauth2; import com.example.project1.config.auth.PrincipalDetails; import com.example.project1.config.oauth2.provider.GoogleUserInfo; import com.example.project1.config.oauth2.provider.NaverUserInfo; import com.example.project1.config.oauth2.provider.OAuth2UserInfo; import com.example.project1.domain.member.UserType; import com.example.project1.entity.member.MemberEntity; import com.example.project1.repository.member.MemberRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; import java.util.Map; @Service @Slf4j @RequiredArgsConstructor public class PrincipalOauth2UserService extends DefaultOAuth2UserService { private final BCryptPasswordEncoder bCryptPasswordEncoder; private final MemberRepository memberRepository; // 구글로부터 받은 userReuest 데이터에 대한 후처리되는 함수 @Override public PrincipalDetails loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { // registrationId로 어떤 OAuth로 로그인 했는지 확인가능 log.info("clientRegistration in PrincipalOauth2UserService : " + userRequest.getClientRegistration() ); log.info("accessToken in PrincipalOauth2UserService : " + userRequest.getAccessToken().getTokenValue() ); OAuth2User oAuth2User = super.loadUser(userRequest); // 구글 로그인 버튼 클릭 →구글 로그인 창 → 로그인 완료 → code 를 리턴(OAuth-Client 라이브러리) → AccessToken 요청 // userRequest 정보 → 회원 프로필 받아야함(loadUser 함수 호출) → 구글로부터 회원 프로필을 받아준다. log.info("getAttributes in PrincipalOauth2UserService : " + oAuth2User.getAttributes()); // 회원가입을 강제로 진행 OAuth2UserInfo oAuth2UserInfo = null; if(userRequest.getClientRegistration().getRegistrationId().equals("google")) { log.info("구글 로그인 요청"); oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes()); } else if(userRequest.getClientRegistration().getRegistrationId().equals("naver")) { log.info("네이버 로그인 요청"); // 네이버는 response를 json으로 리턴을 해주는데 아래의 코드가 받아오는 코드다. // response={id=5SN-ML41CuX_iAUFH6-KWbuei8kRV9aTHdXOOXgL2K0, email=zxzz8014@naver.com, name=전혜영} // 위의 정보를 NaverUserInfo에 넘기면 oAuth2UserInfo = new NaverUserInfo((Map)oAuth2User.getAttributes().get("response")); } else { log.info("구글과 네이버만 지원합니다."); } // 사용자가 로그인한 소셜 서비스(provider)를 가져옵니다. // 예를 들어, "google" 또는 "naver"와 같은 값을 가질 수 있습니다. String provider = oAuth2UserInfo.getProvider(); // 사용자의 소셜 서비스(provider)에서 발급된 고유한 식별자를 가져옵니다. // 이 값은 해당 소셜 서비스에서 유니크한 사용자를 식별하는 용도로 사용됩니다. String providerId = oAuth2UserInfo.getProviderId(); // 예) google_109742856182916427686 String userName = provider + "_" + providerId; String password = bCryptPasswordEncoder.encode("get"); // 사용자의 이메일 주소를 가져옵니다. 소셜 서비스에서 제공하는 이메일 정보를 사용합니다. String email = oAuth2UserInfo.getEmail(); // 사용자의 권한 정보를 설정합니다. UserType. // 여기서는 소셜로그인으로 가입하면 무조건 User로 권한을 주는 방식으로 했습니다. UserType role = UserType.USER; // 이메일 주소를 사용하여 이미 해당 이메일로 가입된 사용자가 있는지 데이터베이스에서 조회합니다. MemberEntity member = memberRepository.findByUserEmail(email); if(member == null) { log.info("OAuth 로그인이 최초입니다."); log.info("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"); log.info("OAuth 자동 회원가입을 진행합니다."); member = MemberEntity.builder() .userName(userName) .userPw(password) .userEmail(email) .userType(role) .provider(provider) .providerId(providerId) .build(); log.info("userEmail in PrincipalOauth2UserService : " + member.getUserEmail()); log.info("userName in PrincipalOauth2UserService : " + member.getUserName()); log.info("userPw in PrincipalOauth2UserService : " + member.getUserPw()); log.info("userType in PrincipalOauth2UserService : " + member.getUserType()); log.info("provider in PrincipalOauth2UserService : " + member.getProvider()); log.info("providerId in PrincipalOauth2UserService : " + member.getProviderId()); memberRepository.save(member); } else { log.info("로그인을 이미 한적이 있습니다. 당신은 자동회원가입이 되어 있습니다."); MemberEntity findUser = memberRepository.findByUserEmail(email); log.info("findUser in PrincipalOauth2UserService : " + findUser); } OAuth2User oAuth2User1 = super.loadUser(userRequest); log.info("getAttributes in PrincipalOauth2UserService : " + oAuth2User1.getAttributes()); // attributes가 있는 생성자를 사용하여 PrincipalDetails 객체 생성 // 소셜 로그인인 경우에는 attributes도 함께 가지고 있는 PrincipalDetails 객체를 생성하게 됩니다. PrincipalDetails principalDetails = new PrincipalDetails(member, oAuth2User.getAttributes()); log.info("principalDetails in PrincipalOauth2UserService : " + principalDetails); return principalDetails; } }package com.example.project1.config.oauth2.provider; public interface OAuth2UserInfo { String getProviderId(); String getProvider(); String getEmail(); String getName(); }package com.example.project1.config.oauth2.provider; import java.util.Map; public class GoogleUserInfo implements OAuth2UserInfo{ // getAttributes()를 받음 private Map<String, Object> attributes; public GoogleUserInfo(Map<String, Object> attributes) { this.attributes = attributes; } @Override public String getProviderId() { return (String) attributes.get("sub"); } @Override public String getProvider() { return "google"; } @Override public String getEmail() { return (String) attributes.get("email"); } @Override public String getName() { return (String) attributes.get("name"); } }package com.example.project1.config.oauth2.provider; import java.util.Map; public class NaverUserInfo implements OAuth2UserInfo{ // oauth2User.getAttributes()를 받음 private Map<String,Object> attributes; // PrincipalOauth2UserService에서 new NaverUserInfo((Map)oAuth2User.getAttributes().get("response"))로 // Oauth2 네이버 로그인 정보를 받아온다. // → {id=5SN-ML41CuX_iAUFH6-KWbuei8kRV9aTHdXOOXgL2K0, email=zxzz8014@naver.com, name=전혜영} public NaverUserInfo(Map<String, Object> attributes) { this.attributes = attributes; } @Override public String getProviderId() { return (String)attributes.get("id"); } @Override public String getProvider() { return "naver"; } @Override public String getEmail() { return (String)attributes.get("email"); } @Override public String getName() { return (String)attributes.get("name"); } }package com.example.project1.config.auth; import com.example.project1.entity.member.MemberEntity; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.oauth2.core.user.OAuth2User; import java.util.ArrayList; import java.util.Collection; import java.util.Map; @Setter @Getter @ToString @NoArgsConstructor @Slf4j public class PrincipalDetails implements UserDetails, OAuth2User { // 일반 로그인 정보를 저장하기 위한 필드 private MemberEntity member; // OAuth2 로그인 정보를 저장하기 위한 필드 // attributes는 Spring Security에서 OAuth2 인증을 수행한 후에 사용자에 대한 추가 정보를 저장하는 데 사용되는 맵(Map)입니다. // OAuth2 인증은 사용자 인증 후에 액세스 토큰(Access Token)을 발급받게 되는데, // 이 토큰을 사용하여 OAuth2 서비스(provider)로부터 사용자의 프로필 정보를 요청할 수 있습니다. // 예를 들어, 소셜 로그인을 사용한 경우에는 attributes에는 사용자의 소셜 서비스(provider)에서 제공하는 프로필 정보가 담겨 있습니다. // 소셜 로그인 서비스(provider)마다 제공하는 프로필 정보가 다를 수 있습니다. // 일반적으로 attributes에는 사용자의 아이디(ID), 이름, 이메일 주소, 프로필 사진 URL 등의 정보가 포함됩니다. /* * 구글의 경우 * { "sub": "100882758450498962866", // 구글에서 발급하는 고유 사용자 ID "name": "John Doe", // 사용자 이름 "given_name": "John", // 이름(이름 부분) "family_name": "Doe", // 성(성(성) 부분) "picture": "https://lh3.googleusercontent.com/a/AAcHTtdzQomNwZCruCcM0Eurcf8hAgBHcgwvbXEBQdw3olPkSg=s96-c", // 프로필 사진 URL "email": "johndoe@example.com", // 이메일 주소 "email_verified": true, // 이메일 주소 인증 여부 "locale": "en" // 지역 설정 } * */ private Map<String, Object> attributes; // 일반 로그인 public PrincipalDetails(MemberEntity member) { this.member = member; } // OAuth2 로그인 public PrincipalDetails(MemberEntity member, Map<String, Object> attributes) { this.member = member; this.attributes = attributes; } // 해당 유저의 권한을 리턴하는 곳 @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> collection = new ArrayList<>(); collection.add(new SimpleGrantedAuthority("ROLE_" + member.getUserType().toString())); log.info("collection : " + collection); return collection; } // 사용자 패스워드를 반환 @Override public String getPassword() { log.info("password : "+ member.getUserPw()); return member.getUserPw(); } // 사용자 이름 반환 @Override public String getUsername() { log.info("id : " + member.getUserEmail()); return member.getUserEmail(); } // 계정 만료 여부 반환 @Override public boolean isAccountNonExpired() { // 만료되었는지 확인하는 로직 // true = 만료되지 않음 return true; } // 계정 잠금 여부 반환 @Override public boolean isAccountNonLocked() { // true = 잠금되지 않음 return true; } // 패스워드의 만료 여부 반환 @Override public boolean isCredentialsNonExpired() { // 패스워드가 만료되었는지 확인하는 로직 // true = 만료되지 않음 return true; } // 계정 사용 가능 여부 반환 @Override public boolean isEnabled() { // 계정이 사용 가능한지 확인하는 로직 // true = 사용 가능 return true; } @Override public Map<String, Object> getAttributes() { log.info("attributes : " + attributes); return attributes; } @Override // OAuth2 인증에서는 사용되지 않는 메서드이므로 null 반환 public String getName() { return null; } } // Oauth2 google로 JWT 발급 @GetMapping("/success-oauth") public ResponseEntity<?> createTokenForGoogle(@AuthenticationPrincipal OAuth2User oAuth2User , @RequestBody MemberDTO member) { Object email = oAuth2User.getAttribute("email"); log.info("oAuth2User : " + email); if(oAuth2User == null) { log.info("받아올 정보가 없습니다 ㅠㅠ"); return ResponseEntity.status(HttpStatus.NOT_FOUND).body("정보가 없어...."); } else { // OAuth2User에서 필요한 정보를 추출하여 UserDetails 객체를 생성합니다. ResponseEntity<TokenDTO> token = memberService.createToken((String) email, member); log.info("token : " + token); return ResponseEntity.ok().body(token); } } // 소셜 로그인 성공시 jwt 반환 // OAuth2User에서 필요한 정보를 추출하여 UserDetails 객체를 생성하는 메서드 public ResponseEntity<TokenDTO> createToken(String userEmail, MemberDTO member) { MemberEntity findUser = memberRepository.findByUserEmail(userEmail); log.info("findUser in MemberService : " + findUser); findUser = MemberEntity.builder() // id를 식별해서 수정 // 이거 없으면 새로 저장하기 됨 // findUser꺼를 쓰면 db에 입력된거를 사용하기 때문에 // 클라이언트에서 userEmail을 전달하더라도 서버에서 기존 값으로 업데이트가 이루어질 것입니다. // 이렇게 하면 userEmail을 수정하지 못하게 할 수 있습니다. .userId(findUser.getUserId()) .userEmail(findUser.getUserEmail()) .userPw(passwordEncoder.encode(findUser.getUserPw())) .userName(findUser.getUserName()) .nickName(member.getNickName()) .userType(findUser.getUserType()) .address(AddressEntity.builder() .userAddr(member.getAddressDTO().getUserAddr()) .userAddrDetail(member.getAddressDTO().getUserAddrDetail()) .userAddrEtc(member.getAddressDTO().getUserAddrEtc()) .build()) .build(); memberRepository.save(findUser); // 권한 정보 추출 List<GrantedAuthority> authorities = getAuthoritiesForUser(findUser); // UserDetails 객체 생성 (사용자의 아이디 정보를 활용) // 첫 번째 인자 : username 사용자 아이디 // 두 번째 인자 : 사용자의 비밀번호 // 세 번째 인자 : 사용자의 권한 정보를 담은 컬렉션 UserDetails userDetails = new User(userEmail, null, authorities); log.info("userDetails in MemberService : " + userDetails); TokenDTO token = jwtProvider.createToken2(userDetails); log.info("token in MemberService : " + token); return ResponseEntity.ok().body(token); } // 소셜 로그인 성공시 JWT 발급 public TokenDTO createToken2(UserDetails userDetails) { long now = (new Date()).getTime(); Date now2 = new Date(); // userDetails.getAuthorities()는 사용자의 권한(authorities) 정보를 가져오는 메서드입니다. // claims.put("roles", userDetails.getAuthorities()) 코드는 사용자의 권한 정보를 클레임에 추가하는 것입니다. // 클레임에는 "roles"라는 키로 사용자의 권한 정보가 저장되며, 해당 권한 정보는 JWT의 페이로드 부분에 포함됩니다. Claims claims = Jwts.claims().setSubject(userDetails.getUsername()); claims.put(AUTHORITIES_KEY, userDetails.getAuthorities()); log.info("claims : " + claims); // access token Date accessTokenExpire = new Date(now + this.accessTokenTime); String accessToken = Jwts.builder() .setSubject(userDetails.getUsername()) .setClaims(claims) .setIssuedAt(now2) .setExpiration(accessTokenExpire) .signWith(key,SignatureAlgorithm.HS256) .compact(); // RefreshToken 생성 Date refreshTokenExpire = new Date(now + this.refreshTokenTime); String refreshToken = Jwts.builder() .setIssuedAt(now2) .setClaims(claims) .setExpiration(refreshTokenExpire) .signWith(key, SignatureAlgorithm.HS256) .compact(); TokenDTO tokenDTO = TokenDTO.builder() .grantType("Bearer ") .accessToken(accessToken) .refreshToken(refreshToken) .userEmail(userDetails.getUsername()) .build(); log.info("tokenDTO in JwtProvider : " + tokenDTO); return tokenDTO; } http // oauth2Login() 메서드는 OAuth 2.0 프로토콜을 사용하여 소셜 로그인을 처리하는 기능을 제공합니다. .oauth2Login() // OAuth2 로그인 성공 이후 사용자 정보를 가져올 때 설정 담당 .userInfoEndpoint() // OAuth2 로그인 성공 시, 후작업을 진행할 서비스 .userService(principalOauth2UserService) .and() .defaultSuccessUrl("/success-oauth");소셜 로그인 후 쇼핑몰 프로젝트라 주소가 필 수라서 JSON을 추가로 받아서 주소를 DB에 저장 후 JWT를 반환해주는 로직을 구성하려고 합니다. 소셜 로그인을 했을 때 PrincipalOauth2UserService여기서 모든 값이 제대로 들어갔고 PrincipalDetails principalDetails = new PrincipalDetails(member, oAuth2User.getAttributes()); log.info("principalDetails in PrincipalOauth2UserService : " + principalDetails); return principalDetails;로그를 찍어본 결과 제대로 값이 로그에 찍혔습니다. 이거를 principalDetails에 보내줬으니 principalDetails 클래스에서도 제대로 받아졌는지 확인해봤습니다. Oaut2User를 상속받아서 오버라이드 한 @Override public Map<String, Object> getAttributes() { log.info("attributes : " + attributes); return attributes; }여기서도 로그에 제대로 값이 나왔습니다.여기서부터가 문제인데 컨트롤러에서 @AuthenticationPrincipal OAuth2User oAuth2User로 소셜 로그인한 정보를 받아와서 토큰을 만들려고 하는데 null이 뜹니다. 별방법을 다했는데 다른 부분은 다 해결을 했는데 이부분이 해결이 안되네요 ㅠㅠ
-
미해결[신규 개정판] 이것이 진짜 크롤링이다 - 기본편
브라우저 꺼짐 방지 문구 관련 오류
브라우저 꺼짐 방지 코드를 작성할때 계속해서 SyntaxError: invalid syntax 나옵니다. 오타는 없는것같은데 혹시 어떤 부분에 오류가 있을까요?
-
미해결한 입 크기로 잘라먹는 타입스크립트(TypeScript)
tsc -v 안되시는분들 이렇게 해보세요 (Mac)
터미널에 brew install typescript 으로 설치 하시고 해보시면 되네요.