묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
CATEGORY_ITEM 클래스를 직접 생성하면 다대일 관계가 될까요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]강의와 자료 내용대로 수업을 듣고 라이브 코딩으로 강사님과 똑같은 구동상황을 확인했습니다.그러다 한 가지 궁금증이 생겨서 질문을 남깁니다.지금 상황에서는 @ManyToMany 어노테이션을 사용해서Category 클래스와 Item 클래스가 다대다 관계를 가져,자동으로 category_item테이블이 생성되는 것으로 보입니다.그렇다면 강사님이 주신 자료에서 회원 테이블 분석표대로CategoryItem클래스를 생성하여 @OnetoMany @ManyToOne 관계로 전환한다면 일대다 다대일 관계가 성립하면서추가로 다른 컬럼을 추가한다거나 하는 게 가능할까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
swr를 사용했을 때 팔로잉, 팔로워 목록을 불러오지 못 하고, 리스트에 limit이 안됩니다!
안녕하세요 제로초님! 노드버드 섹션5 swr 사용해보기 강의까지 진행한 수강생 입니다!항상 질문에 답해주셔서 감사합니다. 올려주신 강의도 잘 보고 있습니다!구글링과 제로초님의 노드버드 깃허브를 꼼꼼히 살펴보았으나 제 지식 부족으로 인하여 질문 올립니다..!swr 라이브러리를 사용 후, 로그인한 다음 프로필 페이지에 가면콘솔 에러가 발생하며, 팔로잉, 팔로워 목록이 없는 문제가 발생합니다.때문에 더 보기 버튼을 클릭해도 더 불러오지 못 했습니다!에러 메시지를 번역하니 다음과 같았습니다.리덕스 탭과 네트워크 탭에서는 에러를 발견하지 못하였습니다!리덕스 탭에서는 해당 사용자의 팔로잉, 팔로워 정보가 들어가 있었고,네트워크 탭에서는 팔로잉, 팔로워 limit이 잘 불러와 집니다.의심이 들어 백엔드 터미널을 확인해보니 팔로잉, 팔로워 목록 limit이 안되고 있었습니다!GET /user/followers?limit=3 401GET /user/followings?limit=3 401에러가 난 코드를 포함해 가장 의심되는 코드를 올립니다!프로필 페이지 profile.js팔로워, 팔로잉 불러오기 구조분해 할당 부분의 ${}코드가 자꾸 깨져서 이 부분만 사진으로 올리겠습니다..!// 실제로 주소 가져오기 : 데이터를 가져오는 API를 호출하는 fetcher 함수 const fetcher = (url) => axios.get(url, { withCredentials: true }).then((result) => result.data ); // 프로필 컴포넌트(사용자 정의 태그) const Profile = () => { // 프로필 페이지에서 로그아웃한 상태일(me가 없을 때)때 메인 페이지로 이동 useEffect(() => { if (!(me && me.id)) { Router.push('/'); } }, [me && me.id]); // 팔로워 목록 더 불러오기 콜백 함수 const loadMoreFollowers = useCallback(() => { setFollowersLimit((prev) => prev + 3); // 기존 limit보다 3 올려주기 }, []); // 팔로잉 목록 더 불러오기 콜백 함수 const loadMoreFollowings = useCallback(() => { setFollowingsLimit((prev) => prev + 3); // 기존 limit보다 3 올려주기 }, []); // 로그인 하지 않은 상태일(me가 없을)때 프로필 페이지로 이동 막기 if (!me) { return '내 정보 로딩중...'; }; /* 팔로워 에러 or 팔로잉 에러 둘 중 하나가 에러났을 때 에러 콘솔하기 */ // Hook은 항상 모두 다 실행되어야 하기에 return은 Hook보다 위에 위치할 수 없다. if (followerError || followingError) { console.error(followerError || followingError); return <div>팔로워/팔로잉 로딩 중 에러가 발생합니다.</div>; } return ( <> <Head> <title>내 프로필 | NodeBird</title> </Head> <AppLayout> {/* ---------- 닉네임 수정 폼 ---------- */} <NicknameEditForm /> {/* ---------- 팔로잉 목록 ---------- */} console.log(followingsData); <FollowList header="팔로잉" data={followingsData} onClickMore={loadMoreFollowings} // SWR에서의 로딩 : 팔로잉 데이터와 팔로잉 에러가 없을 때 loading={!followingsData && !followingError} /> {/* ---------- 팔로워 목록 ---------- */} <FollowList header="팔로워" data={followersData} onClickMore={loadMoreFollowers} // SWR에서의 로딩 : 팔로워 데이터와 팔로워 에러가 없을 때 loading={!followersData && !followerError} /> </AppLayout> </> ); };user.js(백엔드 팔로워, 팔로잉 라우터)// 팔로워 라우터 router.get('/followers', isLoggedIn, async (req, res, next) => { // GET /user/followers try { /* 나를 찾는 함수 */ const user = await User.findOne({ where: { id: req.user.id }}); /* ---------- 만약 내가 없다면 400번대 에러 출력 ---------- */ if (!user) { res.status(403).send('없는 사람을 찾으려고 하시네요?'); } /* 사용자 팔로워 목록 가져오기 */ // limit을 올려주면 그 limit만큼 더 가져오도록 하기 const followers = await user.getFollowers({ limit: parseInt(req.query.limit, 10), }); /* 팔로워 목록을 프론트로 넘기기 */ res.status(200).json(followers); /* ---------- 에러 캐치 ---------- */ } catch (error) { console.error(error); next(error); } }); // 팔로잉 라우터 router.get('/followings', isLoggedIn, async (req, res, next) => { // GET /user/followings try { /* 나를 찾는 함수 */ const user = await User.findOne({ where: { id: req.user.id }}); /* ---------- 만약 내가 없다면 400번대 에러 출력 ---------- */ if (!user) { res.status(403).send('없는 사람을 찾으려고 하시네요?'); } /* 사용자 팔로잉 목록 가져오기 */ // limit을 올려주면 그 limit만큼 더 가져오도록 하기 const followings = await user.getFollowings({ limit: parseInt(req.query.limit, 10), }); /* 팔로잉 목록을 프론트로 넘기기 */ res.status(200).json(followings); /* ---------- 에러 캐치 ---------- */ } catch (error) { console.error(error); next(error); } });FollowList.js// 팔로우 리스트 컴포넌트(사용자 정의 태그) const FollowList = ({ header, data, onClickMore, loading }) => { . . . return ( <List /* 더보기 버튼 */ loadMore={( <div style={{ textAlign: 'center', margin: '10px 0px' }}> <Button onClick={onClickMore} loading={loading}>더 보기</Button> </div> )} . . . /> ); };
-
미해결스프링 핵심 원리 - 고급편
invoke에 대해서 궁금한점이 있습니다.
JDK동적 프록시를 도입후에 사용하게되면 내부에 target 메소드를 직접 호출하는 방식에서 invoke형태로 메소드를 호출하는 방식으로 변경이 되는데 해당 프록시객체의 메소드를 사용할때마다 invoke를 사용하는 방식으로 사용이 되는건가요? 만약에 그렇다면 일반적으로 알기론 일반메소드 호출보다 invoke를 사용한 메소드 호출이 속도면에서 안좋다고 들었는데 이런걸 감안하고 사용을 하는건가요?
-
미해결게임 디자인 패턴 with Unity
GameManager에 씬전환은 왜 문제가 되나요 ?
어떤 점이 문제가 되나요 ?또 다른 기능들을 다 넣어버리면 문제가 된다고 하셨는데 예를 들어 어떤 기능인가요 ?
-
미해결스프링 핵심 원리 - 고급편
InvocationHandler에 대해서 질문이 있습니다.
public class TimeInvocationHandler implements InvocationHandler { private final Object target; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("TimeProxy실행"); log.info("proxy={},proxyclass={}",proxy,proxy.getClass()); long startTime=System.currentTimeMillis(); Object result=method.invoke(target,args); //파라미터 인자값 넘겨줌 long endTime=System.currentTimeMillis(); long resultTime=endTime-startTime; log.info("TimeProxy종료 resultTime={}",resultTime); return result; } }@Test void dynamicA() { AInterface target=new AImpl();//원객체 TimeInvocationHandler handler=new TimeInvocationHandler(target); //프록시 객체 AInterface proxy=(AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(),new Class[]{AInterface.class},handler); proxy.call(); log.info("targetClass={}", target.getClass()); log.info("proxyClass={} proxy={}",proxy, proxy.getClass()); } 해당부분에서 invoke의 파라미터로 넘어오는 proxy의경우 찍어보니우리가 만든 프록시 객체와 동일한 객체로 찍히던데 해당 파라미터를 사용하는경우가 존재하나요?프록시 체인을 사용하더라도 어차피 target을 이용해서 호출하는거기때문에 관련이 없을꺼같고
-
미해결스프링부트 시큐리티 & JWT 강의
쿠키와 세션에 대해 궁금한 부분 있습니다.
유용한 강의 잘 듣고 있습니다. 감사합니다.강의 내용을 듣다가 쿠키와 세션이 헷갈려서 제가 이해한 내용대로 정리했는데, 확인해주실 수 있나요?쿠키와 세션에는 각각 name과 value 필드가 있는데, 쿠키의 value는 세션의 name이 된다. 강의 중 말씀하신 세션 ID '1234' 혹은 '겟인데어'인 세션 name 은 실제로는 유추할 수 없는 UUID의 값이 들어간다.클라이언트가 최초 요청 시에는 세션의 name인 UUID를 생성해 리턴해주고, 그 후 데이터 요청 시에는 쿠키 value를 받아 세션 name이 유효한지를 확인 후 맞으면 유저 정보가 담긴 세션의 value 값을 요청대로 처리한다.추가로 궁금한 건 쿠키의 name 은 자바의 필드처럼 value를 지칭하는 말이 들어가던데, 실제로는 어떤 값이 들어가나요? 정해진 게 있나요 아니면 개발자가 정하기 마음인가요?
-
미해결설계독학맛비's 실전 Verilog HDL Season 1 (Clock부터 Internal Memory까지)
pipeline 질문
질문 1. pipeline 부분에서 설계 프로젝트가 pipeline 구조로 된 8제곱 계산기잖아요?그때 설계 spec이 0~99까지의 입력을 넣어 0^8~99^8을 결과로 만들어내는 모듈인데 이때 궁금한 점이 앞 전에 설계했던 counter를 8제곱 계산기 앞에 붙여도 되나요?0~99까지만 cnt 하는 counter를 앞에 놓고 그 출력을 입력으로 하는 8제곱 계산기로 설계해도 상관없는 건가요? 질문 2. 그리고 testbench에서 @(posedge clk) //afor(i=0;i<100;i=i+1) begin@(negedge clk)i_valid = 1;i_value = i;@(posedge clk) //bend@(negedge clk)i_valid = 0;i_value = 0;라고 코드를 작성하셨는데 이런 문법이 따로 있나요?? 이 코드 자체가 잘 이해되지 않습니다.. (지피티한테 물어봐도요 ㅜ)//a에서 posedge를 받으면 for 문으로 들어간다i=0 을 받고 negedge clk 가 되면 할당한다.//b에서 다음 클럭의 posedge clk를 받으면 다시 //a로 돌아간다.1~3을 반복수행한다. 이런 순서가 맞나요??저는 이런 순서로 생각하고코드를always @(posedge clk) beginfor(i=0;i<100;i=i+1) begin@(negedge clk)i_valid = 1;i_value = i;end 로 바꿔서 해봤거든요 근데 에러나서 돌아가질 안길래.. 제가 보기에는 다른 점이 없어보이는데어떤 점이 문제일까요?
-
해결됨Vue.js + TypeScript 완벽 가이드
composition API에서의 ref
회사에서 사용하고 있는 ref가 너무 궁금해서 영상을 보니 많이 다르네요. 역시 vue2와 vue3의 차이로군요. vue3로 강의 새로 만들어 주시면 좋을것 같습니다. 기대하고 있겠습니다. ^^
-
미해결파이썬을 이용한 IT 해킹과 보안
reg_add.exe 실행이 안됩니다
가상머신에서 reg_add.exe 를 관리자로 실행하려는데 the version of this file is not compatible ... whether you need an x86(32-bit) or x64 version of the program.. 오류가 발생합니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
URL요청 마지막에 "/" 을 포함하니 같은 URL로 매핑 안됩니다
안녕하세요!package hello.springmvc.basic.requestmapping; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MappingController { private Logger log = LoggerFactory.getLogger(getClass()); @RequestMapping("/hello-basic") public String helloBasic(){ log.info("helloBasic"); return "ok"; } }이렇게 /hello-basic으로 매핑 되어있습니다 /hello-basicURL로 요청하면 잘 되지만 강의 3분대 내용처럼 '/'를 추가했더니 같은 요청으로 매핑이 안되고 404 오류가 발생합니다
-
해결됨독하게 시작하는 C 프로그래밍
예제 다운 후 한글 깨짐
안녕하세요, Visual Studio 2022 Community 버젼을 사용하고 있는데 다운 후 파일들을 보니깐 파일이 깨져 있는데 혹시 해결법이 있을까요?
-
미해결스프링 핵심 원리 - 기본편
[IntelliJ] java.lang.ClassNotFoundException:문제 해결
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]Build and run using과 Run tests using을 IntelliJ로 바꿔준다 그래도 해결이 안되면 project structure를 들어간다.modules에서 중복된 main과 test를 제거 한다.문제 해결 됐다.
-
미해결대세는 쿠버네티스 (초급~중급편)
macOS에서 UTM을 통해 실행에러 문제
혹시 저같이 실습환경을 구축하다가 헤매고 계신분들이 있을까봐 글을 등록해둡니다!필자: Apple M1 Pro / macOS Sonoma 14.2.1강사님께서 v1.27 k8s 설치 가이드에서Rocky Linux minimal version으로 링크를 달아주셨습니다! UTM을 통해 실행할때 Error가 발생하시는 분들은 CD/DVD iso 이미지를 설치하셔야 정상적으로 실행됩니다ARM64 - DVD 를 클릭하시면 됩니당
-
해결됨입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
강의노트 내용
수업자료 파트에서 강의노트 파일을 받았는데 비어 있는 파일이라고 나옵니다.
-
해결됨비전공자도 이해할 수 있는 AWS 입문/실전
S3를 활용해 Express 서버에 이미지 업로드 시 Error: Region is missing 발생할 경우
.env 파일에 아래와 같이 리전 변수가 추가되어야 합니다.AWS_REGION=ap-northeast-2
-
미해결Next + React Query로 SNS 서비스 만들기
소켓 사용시 메시지 저장 방식에 대해 궁금합니다!
백엔드 서버에서는 소켓에서 메시지를 받으면 DB에 따로 저장하는 식으로 관리가 되는걸까요?? 아니면 프론트의 저장 공간에 저장하는 방식으로도 해결하는 방법이 있을까요??
-
미해결[신규 개정판] 코딩 입문자를 위한 파이썬 완벽가이드
'와"의 차이
질문에 대한 답변은업무가 바쁘지 않으면, 보통 2~3일 내에 드립니다. 다만 이해하기 어려운 질문은 답변 드리기 어려우니,코드와 오류 메시지를 같이 첨부해 주시기 바랍니다. 별찍기 강의에서 '를 사용하시던데 '와 "의 차이점이 궁금하고, 역으로 별찍기에서 앞에 1칸이 띄어진 상태로 그림이 그려지던데 앞에 띄어진 1칸을 없앨 수 가 있나요?for j in range(1,6): print(' '*(5-j),'*'*j) for h in range(1,6): print(' '*(h-1),'*'*(6-h))
-
미해결AWS Certified Solutions Architect - Associate 자격증 준비하기
안녕하세요 수강기간 연장 부탁드리겠습니다.
안녕하세요업무가 늘어서 강의를 수강하기 어려워졌습니다..강의 연장을 부탁드려도 될까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
port 가 계속 살아있는 문제.
전에 했던 포트가 계속 살아있네요. 왜 그런거죠..? 나갈때 ctrl+z했는데.
-
미해결Spring Boot JWT Tutorial
jjwt 버전을 올렸더니 jwt가 유효하지 않다고 합니다
강의 예제를 보면 springboot 3.2.0 버전이고 java도 17버전으로 기준하여 작성된 것 같습니다.제 jdk 환경도 17버전이고요. cmd 창에서 java -version으로 확인도 했습니다.내려받아 돌려보면 잘 작동합니다. 그런데 jjwt 0.11.5 버전이 아닌 jjwt 0.12.3 버전을 사용해보고 싶어서 코드를 일부 수정했습니다.또한 Bcrypt알고리즘이 이제는 더 이상 안전하지 않다는 얘길 들어서 Argon2 알고리즘을 사용해보고자 했습니다.dependency에 jjwt 0.12.3 버전으로 변경하였고Jwts 이하 메서드가 jjwt에서는 바뀌어서 key에 타입을 명시해주어야 했습니다.PostMan에서 JWT Bearer로 설정하고 encode체크를 하지 않은 채로(이미되어있으니) HS512알고리즘으로 Secret을 입력하고 localhost:8080/api/authenticate 로 요청을 보내봤습니다.{"username": "user","password", "user"}하지만 jjwt 0.11.5 버전일 때는 잘 오던 token 내용이 500error를 내면서 토큰이 유효하지 않다고 합니다;제 코드에서 잘못 한 점이 있을까요? P.S 스스로 gradle refresh, clean build 등을 한 후에 테스트해본 결과jjwt 0.12.3은 0.11.5와 vscode의 REST Client로 api 테스트 시에 강의와 동일한 결과를 보여주었습니다. 그러나 Argon2 알고리즘을 사용하여 생성한 비밀번호를 사용하면 엉뚱하게도 jwt토큰이 유효하지 않다며 401 에러가 났습니다. 강의대로 Bcrypt 알고리즘을 쓰면 없는 일입니다...다만 이런 오류를 내는 연관성을 모르겠습니다 ;; package com.gyull.jwt.jwt; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import org.springframework.security.crypto.keygen.BytesKeyGenerator; @ExtendWith(MockitoExtension.class) @SpringBootTest class JwtApplicationTests { private static final Logger logger = LoggerFactory.getLogger(JwtApplicationTests.class); @Mock private BytesKeyGenerator keyGeneratorMock; private Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8(); @Test void contextLoads() { } //argon2알고리즘으로 비밀번호를 생성하기 위해 테스트를 했습니다 //data.sql로 table에 insert하기 위함. @Test public void encodeWhenEqualPasswordWithCustomParamsThenMatches() { encoder = new Argon2PasswordEncoder(16, 32, 4, 65536, 3); String result = encoder.encode("admin"); logger.info("인코딩완료: "+result); assertTrue(encoder.matches("admin", result)); } } package com.gyull.jwt.jwt.security.config; import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.argon2.Argon2PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.filter.CorsFilter; import com.gyull.jwt.jwt.security.filter.JwtFilter; import com.gyull.jwt.jwt.security.filter.JwtSecurityConfig; import com.gyull.jwt.jwt.security.handler.JwtAccessDeniedHandler; import com.gyull.jwt.jwt.security.handler.JwtAuthenticationEntryPoint; import com.gyull.jwt.jwt.security.provider.TokenProvider; import lombok.RequiredArgsConstructor; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final TokenProvider tokenProvider; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final CorsFilter corsFilter; //Bcrypt 알고리즘은 예제 연습할 때는 쓰지만 연산속도가 발전한 지금은 //취약점이 있기 때문에 Argon2를 비롯한 대체 알고리즘을 더 권장하고 있다. @Bean public PasswordEncoder passwordEncoder(){ return new Argon2PasswordEncoder(16, 32, 4, 65536, 3); } //Spring Security 6.1.x 버전부터는 Lambda DSL의 사용으로 .and()와 같은 메서드가 불필요하다 //Spring Security 7을 염두로 두고 deprecated, removal 되는 메서드가 무척 많았다 @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf((csrf) -> csrf.disable()) // .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) .exceptionHandling((exceptionHandling) -> exceptionHandling .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler(jwtAccessDeniedHandler) ) .headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin())) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests( authorize -> authorize .requestMatchers("/").permitAll() .requestMatchers("/api/hello", "/api/authenticate", "/api/signup").permitAll() .requestMatchers("/admin/**").permitAll() .requestMatchers("/favicon.ico").permitAll() .requestMatchers(PathRequest.toH2Console()).permitAll() .anyRequest().authenticated() ) //Spring Security 6.2.0 javadoc에서는 .apply()메서드가 deprecated되었으니 .with()을 대신 쓰라고 되어있다. //다른 github 예제들은 jwtSecurityConfig를 따로 작성하지 않고 //addFilterBefore을 사용하여 직접 추가해주고 있는데, jwtSecurityConfig 또한 내부적으로 addFilterBefore을 사용해서 httpSecurity에 추가해줄 수도 있다. .with(new JwtSecurityConfig(tokenProvider), customizer -> {}); return http.build(); } } implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.12.3' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.12.3' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.12.3' import javax.crypto.SecretKey; //나머진 동일하여 생략 public String createToken(Authentication authentication){ String authorities = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(",")); long now = (new Date()).getTime(); Date validity = new Date(now + this.tokenValidityInMilliseconds); return Jwts.builder() .subject(authentication.getName()) .claim(AUTHORITIES_KEY, authorities) .signWith((SecretKey) key, Jwts.SIG.HS512) .expiration(validity) .compact(); } public Authentication getAuthentication(String token){ Claims claims = Jwts .parser() .verifyWith((SecretKey) key) .build() .parseSignedClaims(token) .getPayload(); Collection<? extends GrantedAuthority> authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); User principal = new User(claims.getSubject(), "", authorities); return new UsernamePasswordAuthenticationToken(principal, token, authorities); } public boolean validateToken(String token){ try { Jwts.parser().verifyWith((SecretKey) key).build().parseSignedClaims(token); return true; } catch(SecurityException | MalformedJwtException e){ logger.info("잘못된 JWT 서명입니다."); } catch(ExpiredJwtException e){ logger.info("만료된 JWT 토큰입니다."); } catch(UnsupportedJwtException e){ logger.info("지원되지 않는 JWT 토큰입니다."); } catch(IllegalArgumentException e){ logger.info("JWT 토큰이 잘못되었습니다."); } return false; }