묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
th:value=""; 로 이전 데이터 가져오지 못하는 문제
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]@nullable로 잡혀있는 rejectedValue 을 addItemV1에서 new FieldError 매개변수에 넣지 않으니 가격이나 수량에 넣어줬던 값이 error 발생 시 그대로 저장이 안되더라구요. 그래서 구글링 후에bindingResult.addError(new FieldError("item", "price", item.getPrice(), false, null, null, "상품가격 범위 초과(1000~1000000)"));이런 식으로 넣으니까 강의처럼 값이 그대로 넘겨지긴 하네요. 영상에서는 해당 부분말고 상품명까지만 확인한거같은데 참고 부탁드립니다~
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
RedirectAttribute Url인코딩여부
return에 redirect:/ + 값 위와같이 URL매핑주소에 파라미터를 그대로 넘겨서 반환하게 된다면 URL 인코딩에 대한 위험성을 함께 지적하시면서 RedirectAttribute수업을 진행하였는데요.수업 중 status를 통한 유효성 검증 처리만 하고 인코딩 여부를 어떤식으로 확인할 수 있는지에 대한 내용이 생략된거 같습니다.혹시 따로 글로라도 확인시켜주실수는 있을까요?
-
해결됨Flutter 앱 개발 기초
플러터 어뮬레이터가 vs코드나 밖으로 창을 빼면 작동이 안합니다.
수업 유용하게 잘 듣고 있습니다^^이렇게 안드로이드 스튜디오 안에서 어뮬레이터를 실행하면 실행에 이상이 없습니다. 이렇게 어뮬레이터만 밖에서 실행하거나 비쥬얼 스튜디오 코드에서 어뮬레이터 실행하면 어뮬레이터가 잘 돌아가지가 않습니다. 구글링 해서 문의했는데 전원 버튼을 누르거나 디바이스 'wipe data'도 했는데 똑같이 어뮬레이터가 실행이 안됩니다. 그래서 어뮬레이터를 삭제한 뒤 다시 만들었는데 안되네요.강사님께서 "재설치시 안드로이드 os 버전도 올려보시길 권장드립니다." 하셨는데 제가 코린이라 os버전 어떻게 하는지 어렵네요 ㅠㅠㅠ
-
미해결스프링 핵심 원리 - 고급편
데코레이터 구성 시 AOP를 사용해도 되는가
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]안녕하세요. 강의 잘 듣고 있습니다.질문은 "단순 데코레이터를 만들고 싶을 때 SpringAOP를 사용해도 되는가?"입니다.질문 매뉴얼의 답이 없는 질문에 포함되는 것 같아 부가설명을 적어보겠습니다. 개인적으로 이전부터 관심사와 의존성을 분리하고 싶어 데코레이터 패턴을 한번씩 쓸 때가 있었습니다.예를 들면 다음과 같습니다.게시글이 작성되면 특정 유저들에게 알림 메세지를 전송해야 한다.게시글이 수정되면 읽기 전용 모델의 캐시를 갱신해야 한다.그럼 [컨트롤러 - 알림데코레이터 - 캐시데코레이터 - 서비스 - 리포지토리 ]가 되는 거죠.이 때 데코레이터-서비스 체인을 구성해야 하는데 2가지 방법이 있었습니다.@Configuration에서 매뉴얼하게 체인 구성한 뒤 빈 생성가장 앞단의 데코레이터에 Primary를 달고 이후 순서에 따라 생성자 파라미터 주입시 @Qualifier로 구현체 주입1번 같은 경우에는 각 데코레이터마다 의존성이 많아질 수록 작성해야 하는 코드가 많아져서 제외를 했습니다.그래서 2번 방법을 사용하고 있고 다음과 같은 문제를 대면했습니다.특정 구현체가 뒷 순서 구현체를 알아야 한다. (의존성 발생) 그것도 컴파일 에러가 나지 않는 문자열(빈 이름)의 형태로.public class PostServiceMessageDecorator implements PostService { private final PostService postService; public PostServiceMessageDecorator( @Qualifier("postServiceCacheDecorator") PostService postService ) { this.postService = postService; } }체인 순서를 구성하는 것이 다소 번거롭고, 순서가 변경되거나 추가, 제거되면 코드를 바꿔야 한다. (다시 한 번 컴파일 에러가 나지 않는 문자열의 형태로)서비스 내에서 데코레이터가 붙지 않는 메소드도 구현을 해줘야 한다.그런데 SpringAOP를 사용하면 3가지 문제를 모두 해결할 수 있는 것이 아닌가 하는 생각이 듭니다.포인트컷도 잡는 것만 잡으면 되니까 데코레이터가 굳이 안붙어도 되는 메소드를 구현할 필요도 없구요.Order로 순서도 간편하게 변경이 가능하니까요.앞서 강의에서 패턴은 의도가 중요하지 실제 구현체는 다양한 방법으로 구현될 수 있다고 하신 말씀이 머릿속에 맴도는데...Aspect를 만들고 네이밍만 EntityServiceSomethingDecorator 라고 이름만 붙이면 되는게 아닌가 하는 생각이 듭니다.그러나 이 방법을 사용하는데 약간의 거부감이 있는데 AOP가 태생적으로 횡단 관심사를 해결하기 위한 기술이라는 사실 때문입니다.저는 흩어져 있는 공통 관심사 코드를 여기 저기 작성하지 않고 한 군데에서 작성하도록 한 게 개발 의도라고 생각했거든요.하지만 예시의 경우는 흩어져 있는 관심사가 아니라 특정 로직에 부가 로직을 몇 개 붙였다 뗐다 하고 싶을 뿐입니다.그렇다면 단순 데코레이터를 만들기 위해 SpringAOP를 사용하는 것은 SpringAOP의 개발 의도와는 약간 다른 사용법이 될 수 있고,보통 특정 기능을 개발 의도와 다른 방향으로 사용하면 예상하지 못한 부작용이 발생하더라구요.이 지점에서 혹시 인사이트를 얻을 수 있을지 질문을 올려봅니다.질문을 조금 다르게 얘기하면 "특정 메서드 혹은 클래스만을 위한 Aspect를 만들어도 되는가?"가 되겠네요.만약 이 경우에 AOP사용은 지양하는 것이 좋다고 생각하신다면, 매뉴얼하게 데코레이터 클래스를 작성하는 것 외에 권장하실만한 방법이 있을까요?
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
IBasePaginationRepository를 U로 바꾼 이유가 궁금해요.
U로 바꾸고 extends를 하면 똑같이 쓸 수 있다고 하신 말씀은 무슨 뜻인지 이해를 했는데요, 왜 그렇게 써야 하는지를 잘 모르겠어요. 그냥 그럴 수 있다는 걸 알려주신 걸까요? 아니면 제너릭으로 U를 쓰고 extends해서 쓰는 게 어떤 이득이 있는 건가요?
-
미해결실전! 스프링 데이터 JPA
의존성 관련 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 의존성에 대해 갑자기 헷갈려서 총 4가지 질문드립니다.MemberService에 아래와 같은 메서드(Member findMemberById(Long id))가 있고 PostService(다른 Service 클래스)에서 memberId로 Member를 조회해야 할 일이 있습니다. 이 경우 아래 메서드를 이용하기 위해 PostService 클래스에서 private final MemberService memberService 형태로 멤버변수로 포함한다고 가정하겠습니다.(memberService.findMemberByMember(id) 로 이용하기위해)public Member findMemberById(Long id) { return memberRepository.findById(id) .orElseThrow(() -> new BusinessLogicException(MEMBER_NOT_FOUND)); } Q1) PostService에서 MemberService를 생성자 주입을 통해 받아서 이용할 경우 MemberService의 의존성(예를 들면 MemberRepository 등의 MemberService 클래스에서 사용하는 클래스들)까지 PostService에 포함되는 것일까요? Q2) 의존성이라는게 단순히 생성자 주입으로 받았던 멤버 클래스들 뿐 아니라 내부 메소드에서 매개변수로 받은 클래스가 있다면 이 또한 의존성인가요?(import 로 포함된 클래스들을 모두 의존클래스로 보면 될지, 아니면 내부에서 사용하는 모든 객체를 의존성으로 보면될지 >> 같은 패키지의 경우 import 안하는걸 고려했을때 내부에서 객체로 이용하지만 같은 패키지라 import 안되는걸 고려) Q3-1) Q1의 답변에서 MemberService의 의존성까지 PostService에 포함되는 거라면 PostService에서 Member를 조회하기 위해서는 memberService.findMemberById(id) 로 조회하기 보단 MemberRepository를 주입받아서 memberRepository.findById(id).orElseThrow(() -> new BusinessLogicException(MEMBER_NOT_FOUND)); 형태로 변경하는게 맞을지? Q3-2) Q1의 답변에서 MemberService의 의존성까지 PostService에 포함되는게 아니라면 PostService에서 MemberService가 아닌 MemberRepository로 조회하는게 맞을지? Member 조회가 여러곳에서 사용되고 Member findMemberById(Long id) 메소드의 내용이 여러곳에서 중복되는 걸 생각했을때처음에는 단순히 member 조회가 여러곳에서 일어나고 공통된 내용이 반복되어 MemberService의 findMemberById 로 묶어서 사용하는게 맞다고 생각했는데 의문이 들었고 예전에 의존성은 최대한 줄이라고 하셨던게 생각나서 질문드립니다.
-
미해결
3~4 통합 교안
안녕하세요 이번에 빅데이터 분석 기사 필기 1~2와 3~4 강의 모두 신청했습니다.3~4 통합 교안을 보내주시면 정말 감사할 것 같습니다.이메일 주소는 goodtc377@gmail.com입니다.감사합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
[section09] Quiz Ant-design DatePicker library
안녕하세요 section09 퀴즈에서 Ant-design Date picker 를 사용하는 과정에서 질문이 생겨 여쭤봅니다. import { DatePicker} from 'antd'; import type { DatePickerProps } from 'antd'; import { useState } from 'react'; export default function AndDesignPage() { const [date, setDate] = useState(''); const [month, setMonth] = useState(''); const onChange: DatePickerProps['onChange'] = (date, dateString) => { console.log(date); setDate(dateString); setMonth(date?.$M + 1);👈👈👈 1️⃣ }; return ( <> <h1>Q2. DatePicker</h1> <span> <DatePicker onChange={onChange} />👈👈👈 2️⃣ </span> {month && ( <> <p>선택날짜 : {date}</p> <p>{month}월을 선택하셨습니다.</p> </> )} </> ); } 전체 코드는 위와 같고 1️⃣,2️⃣번에 빨간 줄이 쳐졌습니다. 1️⃣번은 아래와 같은 메세지가 뜹니다.console.log(date)를 했을 때 아래와 같이 콘솔이 나와서 $M을 활용했는데 작동도 잘 되고 콘솔메세지에도 따로 에러가 뜨지는 않습니다. 2️⃣번은 아래와 같은 문제입니다에러메세지 전문은 다음과 같습니다.(alias) const DatePicker: PickerComponentClass<PickerProps<Dayjs> & { status?: "" | "warning" | "error" | undefined; hashId?: string | undefined; popupClassName?: string | undefined; rootClassName?: string | undefined; }, unknown> & { WeekPicker: import("./generatePicker/interface").PickerComponentClass<Omit<PickerProps<Dayjs> & { status?: "" | "warning" | "error" | undefined; hashId?: string | undefined; popupClassName?: string | undefined; rootClassName?: string | undefined; }, "picker">, unknown>; MonthPicker: import("./generatePicker/interface").PickerComponentClass<Omit<PickerProps<Dayjs> & { status?: "" | "warning" | "error" | undefined; hashId?: string | undefined; popupClassName?: string | undefined; rootClassName?: string | undefined; }, "picker">, unknown>; YearPicker: import("./generatePicker/interface").PickerComponentClass<Omit<PickerProps<Dayjs> & { status?: "" | "warning" | "error" | undefined; hashId?: string | undefined; popupClassName?: string | undefined; rootClassName?: string | undefined; }, "picker">, unknown>; RangePicker: import("./generatePicker/interface").PickerComponentClass<BaseRangePickerProps<Dayjs> & { dropdownClassName?: string | undefined; popupClassName?: string | undefined; }, unknown>; TimePicker: import("./generatePicker/interface").PickerComponentClass<Omit<Omit<import("rc-picker/lib/Picker").PickerTimeProps<Dayjs>, "locale" | "generateConfig" | "hideHeader" | "components" | "hourStep"> & { locale ... import DatePicker 'DatePicker' cannot be used as a JSX component. Its instance type 'Component<PickerProps<Dayjs> & { status?: "" | "warning" | "error" | undefined; hashId?: string | undefined; popupClassName?: string | undefined; rootClassName?: string | undefined; }, unknown, any> & CommonPickerMethods' is not a valid JSX element. The types returned by 'render()' are incompatible between these types. Type 'React.ReactNode' is not assignable to type 'import("/Users/bible/Bible_Highting/codecamp-frontend-bible/class_quiz/node_modules/@types/react-transition-group/node_modules/@types/react/index").ReactNode'. Type '{}' is not assignable to type 'ReactNode'.ts(2786) 1️⃣,2️⃣번 모두 작동에는 이상이 없으나 빨간줄의 원인과 해결방법을 알고싶어 질문드립니다. 감사합니다!
-
미해결
이벤트 어떻게 받나요?
3/8~3/21에 행사한 이벤트에서 로드맵 한 개를 구매하였고 3/28일에 혜택을 지급해준다 하였는데어디서 받는지 궁굼하여 이렇게 글을 올립니다.https://www.inflearn.com/pages/newsemester-230306?utm_source=kakao_friend&utm_medium=social&utm_campaign=traffic_new_user_chatbot&utm_content=20230314_&utm_term=0_MK
-
해결됨스프링부트 JUnit 테스트 - 시큐리티를 활용한 Bank 애플리케이션
변경된 시큐리티의 filterChain에서 and() 메서드 사용은 권장하지 않나요?
기존 상속 받아서 사용하던 것 처럼 변경된 filterChain에서도 and() 메서드를 사용해서 옵션들을 빌더 연결 패턴 처럼 사용할 수 있던데 변경된 시큐리티에선 권장하지 않는 방법인지, 아니면 가독성 때문인지 궁금합니다! @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { log.debug("디버그: filterChain 빈 등록됨"); http.headers().frameOptions().disable() .and() .csrf().disable() .cors().configurationSource(configurationSource()) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .formLogin().disable() .httpBasic().disable() .apply(new CustomSecurityFilterManager()) .and() .exceptionHandling().authenticationEntryPoint((request, response, authException) -> { CustomResponseUtil.fail(response, "로그인을 진행해 주세요", HttpStatus.UNAUTHORIZED); }) .and() .exceptionHandling().accessDeniedHandler((request, response, e) -> { CustomResponseUtil.fail(response, "권한이 없습니다", HttpStatus.FORBIDDEN); }) .and() .authorizeRequests() .antMatchers("/api/s/**").authenticated() .antMatchers("/api/admin/**").hasRole("" + UserEnum.ADMIN) .anyRequest().permitAll(); return http.build(); }
-
미해결10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트
1-A 메모리 초과
안녕하세요 큰돌님.1-A문제를 makePermutation(재귀)을 통하여 작성해봤습니다.vector<int> v를 순서 섞기를 통하여 앞단의 7개 값의 합이 100인경우를 추려낸 다음, vector<int> ret에 memcpy를 통하여 복사를 진행하였습니다.결론적으로는 메모리 초과로 문제해결이 안되었는데, 합이 100인 1건의 사례에 대해서만 memcpy를 사용하였고, exit(0)를 통하여 프로그램을 종료하였는데 메모리 초과가 되는 이유를 모르겠습니다.한번 봐주시면 감사하겠습니다. <오답 - 메모리 초과>http://boj.kr/e8517aff3dde4152bf20a574308f7f76<정답 - 직접 작성>http://boj.kr/ce9f91b1272f4fe7beb54ddff446104c
-
해결됨스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
애플리케이션 런 했을때 에러 발생
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.안녕하세요 vs코드에서 자바 배우려고 intellj(무료버전)를 설치 후 강의를 따라가고 있습니다. 그런데 HelloSpringApplication을 런 했을때 3가지 에러가 발생하는데 핸들링 부탁드립니다.크게는 이 3가지가 나오고, 이건 첫번째 에러인데 해결하지 못했습니다 ㅠㅠ답변 부탁드립니다.
-
해결됨스프링 핵심 원리 - 기본편
test 쪽만 들어가면 앞에 이상한 코드가 잔뜩 들어가는데 도저히 수정을 못하겠습니다.
강사님 화면은 위쪽처럼 깔끔한데 저는 아래쪽에 DEBUG라는 메시지가 잔뜩 나옵니다. 여기저기 아무리 뒤져봐도 해결책을 찾지 못하고 있습니다. 설정 파일을 잘못 건드려서 그런걸까요?
-
해결됨토비의 스프링 부트 - 이해와 원리
회사 비지니스 공통업무처리 관련 유용한 라이브러리 들이 있는지 여쭤봅니다
안녕하세요 토비님~이번에도 강의 내용과 상관 없는 질문 드립니다회사에서 타임리프 + JPA + 마이바티스 + 스프링/스프링부트 + 오라클 환경에서 개발하고 있습니다 MVC CRUD, API 송수신, 특이업무를 제외하고는 보통 회사에서 일어나는 공통업무는 아래와 같은 부분이 있다고 생각 합니다*.엑셀다운*.엑셀업로드*.이메일전송(첨부파일포함)*.PDF다운*.FAX전송*.출력 다른 분들이 먼저 개발해 놓은 소스를 참고해 가며개발 수정 운영을 하고 있는데요 제가 느끼기에는 뭔가 불필요한 소스 코드가 많고 긴 건 아닐까?..누군가 잘 만들어 놓은 라이브러리 메서드에 파라미터만 담아주고호출 하면 되진 않을까 생각이 들었습니다 혹시, 아래와 같은 공통 업무 사항들에 대해서 스프링 진영에서 Util 성격으로 잘 만들어 미리 만들어 놓은 좋은 라이브러리가 있지는 않 을까 생각이 듭니다 (엑셀업로드,다운로드/이메일송수신/PDF/FAX/출력 .. )회사마다 환경이나 요구 상황에 따라서 다르겠지만, 토비님은 이런 공통 비지니스 업무 관련 엑셀업로드,다운로드/이메일송수신/PDF/FAX/출력 관련 공통 비지니스 업무 관련해서 스프링 진영에서 혹시 이미 만들어 놓은 라이브러리 의존 관계를 추가해서사용하고 계시는 부분이 있나 여쭤봅니다 만약 사용하고 계신다면 어떠 어떠한 것들이 있는지 소개 부탁 드립니다 급한 질문 아닙니 시간 나 실 때알려주시면 감사하겠습니다. 수고하세요.--█●●--------------------------------------------#엑셀#이메일#PDF#FAX#출력#스프링#스프링부트#부트#spring #sping-boot#springboot#토비#공통#라이브러리--█●●--------------------------------------------
-
해결됨10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트
4-M stack으로 구현시 반례
안녕하세요 큰돌님~!http://boj.kr/c7f8555cf2714400bcd11b71d55d803d4-M 문제 stack으로 구현 해 보려구 도전 하다가 49% 에서 틀렸다고 나와서해당 로직의 반례가 발생할 수 있는 부분이 어디인지 피드백 받고자 질문드립니다.제가 구현하고자 했던 규칙은 이렇습니다.p가 i를 만나면 pop;k가 a를 만나면 pop;c가 h를 만나면 c를 pop하고 h를 push;h가 u를 만나면 (h를) pop;짝짓기 문제 같아서 stack을 떠올렷는데, 해설코드는 queue더라구요? 연습겸 stack을 사용한 구현도 완성하고 싶습니다!답변 주시면 감사하겠습니다.
-
해결됨[신규 개정판] 이것이 진짜 크롤링이다 - 실전편 (인공지능 수익화)
자바스크립트로 된 링크 가져오는 방법이 궁금합니다.
토요코인 수업 중에 위에 보는 것과 같이 자바스크립트로 링크가 걸려있는 경우 실제 url을 크롤링 하려면 어떻게 해야 하나요?
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
마지막 강의보니 강의가 더나올거 같은데 언제 추가되나요?
보너스 강의가 약간 더 남았습니다~ 조금만 기다려주세요! Optimistic Response + Form + 글로벌 캐시관리를 한번에 보여주는 "리뷰 작성 페이지" 강의가 9월이 가기전에 추가될 계획입니다! 기대해주세요! (지금까지 배운 내용을 한번에 조합하는 강의입니다!)이렇게 글을 쓰셨던데 빨리 배워보고 싶네요 ㅠㅠ강의가 너무 좋아서 그런데 중급 이상강의 혹시 하나더 나올 계획은 없나요? 이 강의보단 조금더 복잡한... 너무 기다려집니다 ㅠㅠ 항상 감사합니다.
-
미해결스프링 시큐리티 OAuth2
Spring Authorization Server 1.0.1 state 문의
authorization_code grantType 으로 인가를 진행한다고 가 정하였을때 authorization_code 발급 요청에서 사용한 state 와 token 발급 요청해서 사용하는 state가 다르면 token 발급이 실패 해야 하는데 성공하고 있어서 분석해보니 oauth2_authorization 테이블의 state 필드가 null 로 저장되고 있습니다. JdbcOAuth2AuthorizationService를 사용하지 않고 커스텀한 구현체를 만들어서 사용하고 있는것이 원인일 수 있어 디버깅 해보니 OAuth2Authorization 의 소스를 분석해보면 state 가 포함되고 있지 않아서 저장이 안되고 있는데 아직 Spring Authorization Server는 state 가 구현되지 않은 걸까요? 추가로 소스를 분석해보니 nonce 도 아직 구현되지 않은거 같습니다.Spring Authorization Server 1.0.1 의 OAuth2Authorization 소스package org.springframework.security.oauth2.server.authorization; import java.io.Serializable; import java.time.Instant; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; import org.springframework.lang.Nullable; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.oauth2.core.OAuth2Token; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.util.SpringAuthorizationServerVersion; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** * A representation of an OAuth 2.0 Authorization, which holds state related to the authorization granted * to a {@link #getRegisteredClientId() client}, by the {@link #getPrincipalName() resource owner} * or itself in the case of the {@code client_credentials} grant type. * * @author Joe Grandja * @author Krisztian Toth * @since 0.0.1 * @see RegisteredClient * @see AuthorizationGrantType * @see OAuth2Token * @see OAuth2AccessToken * @see OAuth2RefreshToken */ public class OAuth2Authorization implements Serializable { private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; private String id; private String registeredClientId; private String principalName; private AuthorizationGrantType authorizationGrantType; private Set<String> authorizedScopes; private Map<Class<? extends OAuth2Token>, Token<?>> tokens; private Map<String, Object> attributes; protected OAuth2Authorization() { } /** * Returns the identifier for the authorization. * * @return the identifier for the authorization */ public String getId() { return this.id; } /** * Returns the identifier for the {@link RegisteredClient#getId() registered client}. * * @return the {@link RegisteredClient#getId()} */ public String getRegisteredClientId() { return this.registeredClientId; } /** * Returns the {@code Principal} name of the resource owner (or client). * * @return the {@code Principal} name of the resource owner (or client) */ public String getPrincipalName() { return this.principalName; } /** * Returns the {@link AuthorizationGrantType authorization grant type} used for the authorization. * * @return the {@link AuthorizationGrantType} used for the authorization */ public AuthorizationGrantType getAuthorizationGrantType() { return this.authorizationGrantType; } /** * Returns the authorized scope(s). * * @return the {@code Set} of authorized scope(s) * @since 0.4.0 */ public Set<String> getAuthorizedScopes() { return this.authorizedScopes; } /** * Returns the {@link Token} of type {@link OAuth2AccessToken}. * * @return the {@link Token} of type {@link OAuth2AccessToken} */ public Token<OAuth2AccessToken> getAccessToken() { return getToken(OAuth2AccessToken.class); } /** * Returns the {@link Token} of type {@link OAuth2RefreshToken}. * * @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if not available */ @Nullable public Token<OAuth2RefreshToken> getRefreshToken() { return getToken(OAuth2RefreshToken.class); } /** * Returns the {@link Token} of type {@code tokenType}. * * @param tokenType the token type * @param <T> the type of the token * @return the {@link Token}, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <T extends OAuth2Token> Token<T> getToken(Class<T> tokenType) { Assert.notNull(tokenType, "tokenType cannot be null"); Token<?> token = this.tokens.get(tokenType); return token != null ? (Token<T>) token : null; } /** * Returns the {@link Token} matching the {@code tokenValue}. * * @param tokenValue the token value * @param <T> the type of the token * @return the {@link Token}, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <T extends OAuth2Token> Token<T> getToken(String tokenValue) { Assert.hasText(tokenValue, "tokenValue cannot be empty"); for (Token<?> token : this.tokens.values()) { if (token.getToken().getTokenValue().equals(tokenValue)) { return (Token<T>) token; } } return null; } /** * Returns the attribute(s) associated to the authorization. * * @return a {@code Map} of the attribute(s) */ public Map<String, Object> getAttributes() { return this.attributes; } /** * Returns the value of an attribute associated to the authorization. * * @param name the name of the attribute * @param <T> the type of the attribute * @return the value of an attribute associated to the authorization, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <T> T getAttribute(String name) { Assert.hasText(name, "name cannot be empty"); return (T) this.attributes.get(name); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } OAuth2Authorization that = (OAuth2Authorization) obj; return Objects.equals(this.id, that.id) && Objects.equals(this.registeredClientId, that.registeredClientId) && Objects.equals(this.principalName, that.principalName) && Objects.equals(this.authorizationGrantType, that.authorizationGrantType) && Objects.equals(this.authorizedScopes, that.authorizedScopes) && Objects.equals(this.tokens, that.tokens) && Objects.equals(this.attributes, that.attributes); } @Override public int hashCode() { return Objects.hash(this.id, this.registeredClientId, this.principalName, this.authorizationGrantType, this.authorizedScopes, this.tokens, this.attributes); } /** * Returns a new {@link Builder}, initialized with the provided {@link RegisteredClient#getId()}. * * @param registeredClient the {@link RegisteredClient} * @return the {@link Builder} */ public static Builder withRegisteredClient(RegisteredClient registeredClient) { Assert.notNull(registeredClient, "registeredClient cannot be null"); return new Builder(registeredClient.getId()); } /** * Returns a new {@link Builder}, initialized with the values from the provided {@code OAuth2Authorization}. * * @param authorization the {@code OAuth2Authorization} used for initializing the {@link Builder} * @return the {@link Builder} */ public static Builder from(OAuth2Authorization authorization) { Assert.notNull(authorization, "authorization cannot be null"); return new Builder(authorization.getRegisteredClientId()) .id(authorization.getId()) .principalName(authorization.getPrincipalName()) .authorizationGrantType(authorization.getAuthorizationGrantType()) .authorizedScopes(authorization.getAuthorizedScopes()) .tokens(authorization.tokens) .attributes(attrs -> attrs.putAll(authorization.getAttributes())); } /** * A holder of an OAuth 2.0 Token and it's associated metadata. * * @author Joe Grandja * @since 0.1.0 */ public static class Token<T extends OAuth2Token> implements Serializable { private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; protected static final String TOKEN_METADATA_NAMESPACE = "metadata.token."; /** * The name of the metadata that indicates if the token has been invalidated. */ public static final String INVALIDATED_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("invalidated"); /** * The name of the metadata used for the claims of the token. */ public static final String CLAIMS_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("claims"); private final T token; private final Map<String, Object> metadata; protected Token(T token) { this(token, defaultMetadata()); } protected Token(T token, Map<String, Object> metadata) { this.token = token; this.metadata = Collections.unmodifiableMap(metadata); } /** * Returns the token of type {@link OAuth2Token}. * * @return the token of type {@link OAuth2Token} */ public T getToken() { return this.token; } /** * Returns {@code true} if the token has been invalidated (e.g. revoked). * The default is {@code false}. * * @return {@code true} if the token has been invalidated, {@code false} otherwise */ public boolean isInvalidated() { return Boolean.TRUE.equals(getMetadata(INVALIDATED_METADATA_NAME)); } /** * Returns {@code true} if the token has expired. * * @return {@code true} if the token has expired, {@code false} otherwise */ public boolean isExpired() { return getToken().getExpiresAt() != null && Instant.now().isAfter(getToken().getExpiresAt()); } /** * Returns {@code true} if the token is before the time it can be used. * * @return {@code true} if the token is before the time it can be used, {@code false} otherwise */ public boolean isBeforeUse() { Instant notBefore = null; if (!CollectionUtils.isEmpty(getClaims())) { notBefore = (Instant) getClaims().get("nbf"); } return notBefore != null && Instant.now().isBefore(notBefore); } /** * Returns {@code true} if the token is currently active. * * @return {@code true} if the token is currently active, {@code false} otherwise */ public boolean isActive() { return !isInvalidated() && !isExpired() && !isBeforeUse(); } /** * Returns the claims associated to the token. * * @return a {@code Map} of the claims, or {@code null} if not available */ @Nullable public Map<String, Object> getClaims() { return getMetadata(CLAIMS_METADATA_NAME); } /** * Returns the value of the metadata associated to the token. * * @param name the name of the metadata * @param <V> the value type of the metadata * @return the value of the metadata, or {@code null} if not available */ @Nullable @SuppressWarnings("unchecked") public <V> V getMetadata(String name) { Assert.hasText(name, "name cannot be empty"); return (V) this.metadata.get(name); } /** * Returns the metadata associated to the token. * * @return a {@code Map} of the metadata */ public Map<String, Object> getMetadata() { return this.metadata; } protected static Map<String, Object> defaultMetadata() { Map<String, Object> metadata = new HashMap<>(); metadata.put(INVALIDATED_METADATA_NAME, false); return metadata; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } Token<?> that = (Token<?>) obj; return Objects.equals(this.token, that.token) && Objects.equals(this.metadata, that.metadata); } @Override public int hashCode() { return Objects.hash(this.token, this.metadata); } } /** * A builder for {@link OAuth2Authorization}. */ public static class Builder implements Serializable { private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; private String id; private final String registeredClientId; private String principalName; private AuthorizationGrantType authorizationGrantType; private Set<String> authorizedScopes; private Map<Class<? extends OAuth2Token>, Token<?>> tokens = new HashMap<>(); private final Map<String, Object> attributes = new HashMap<>(); protected Builder(String registeredClientId) { this.registeredClientId = registeredClientId; } /** * Sets the identifier for the authorization. * * @param id the identifier for the authorization * @return the {@link Builder} */ public Builder id(String id) { this.id = id; return this; } /** * Sets the {@code Principal} name of the resource owner (or client). * * @param principalName the {@code Principal} name of the resource owner (or client) * @return the {@link Builder} */ public Builder principalName(String principalName) { this.principalName = principalName; return this; } /** * Sets the {@link AuthorizationGrantType authorization grant type} used for the authorization. * * @param authorizationGrantType the {@link AuthorizationGrantType} * @return the {@link Builder} */ public Builder authorizationGrantType(AuthorizationGrantType authorizationGrantType) { this.authorizationGrantType = authorizationGrantType; return this; } /** * Sets the authorized scope(s). * * @param authorizedScopes the {@code Set} of authorized scope(s) * @return the {@link Builder} * @since 0.4.0 */ public Builder authorizedScopes(Set<String> authorizedScopes) { this.authorizedScopes = authorizedScopes; return this; } /** * Sets the {@link OAuth2AccessToken access token}. * * @param accessToken the {@link OAuth2AccessToken} * @return the {@link Builder} */ public Builder accessToken(OAuth2AccessToken accessToken) { return token(accessToken); } /** * Sets the {@link OAuth2RefreshToken refresh token}. * * @param refreshToken the {@link OAuth2RefreshToken} * @return the {@link Builder} */ public Builder refreshToken(OAuth2RefreshToken refreshToken) { return token(refreshToken); } /** * Sets the {@link OAuth2Token token}. * * @param token the token * @param <T> the type of the token * @return the {@link Builder} */ public <T extends OAuth2Token> Builder token(T token) { return token(token, (metadata) -> {}); } /** * Sets the {@link OAuth2Token token} and associated metadata. * * @param token the token * @param metadataConsumer a {@code Consumer} of the metadata {@code Map} * @param <T> the type of the token * @return the {@link Builder} */ public <T extends OAuth2Token> Builder token(T token, Consumer<Map<String, Object>> metadataConsumer) { Assert.notNull(token, "token cannot be null"); Map<String, Object> metadata = Token.defaultMetadata(); Token<?> existingToken = this.tokens.get(token.getClass()); if (existingToken != null) { metadata.putAll(existingToken.getMetadata()); } metadataConsumer.accept(metadata); Class<? extends OAuth2Token> tokenClass = token.getClass(); this.tokens.put(tokenClass, new Token<>(token, metadata)); return this; } protected final Builder tokens(Map<Class<? extends OAuth2Token>, Token<?>> tokens) { this.tokens = new HashMap<>(tokens); return this; } /** * Adds an attribute associated to the authorization. * * @param name the name of the attribute * @param value the value of the attribute * @return the {@link Builder} */ public Builder attribute(String name, Object value) { Assert.hasText(name, "name cannot be empty"); Assert.notNull(value, "value cannot be null"); this.attributes.put(name, value); return this; } /** * A {@code Consumer} of the attributes {@code Map} * allowing the ability to add, replace, or remove. * * @param attributesConsumer a {@link Consumer} of the attributes {@code Map} * @return the {@link Builder} */ public Builder attributes(Consumer<Map<String, Object>> attributesConsumer) { attributesConsumer.accept(this.attributes); return this; } /** * Builds a new {@link OAuth2Authorization}. * * @return the {@link OAuth2Authorization} */ public OAuth2Authorization build() { Assert.hasText(this.principalName, "principalName cannot be empty"); Assert.notNull(this.authorizationGrantType, "authorizationGrantType cannot be null"); OAuth2Authorization authorization = new OAuth2Authorization(); if (!StringUtils.hasText(this.id)) { this.id = UUID.randomUUID().toString(); } authorization.id = this.id; authorization.registeredClientId = this.registeredClientId; authorization.principalName = this.principalName; authorization.authorizationGrantType = this.authorizationGrantType; authorization.authorizedScopes = Collections.unmodifiableSet( !CollectionUtils.isEmpty(this.authorizedScopes) ? new HashSet<>(this.authorizedScopes) : new HashSet<>() ); authorization.tokens = Collections.unmodifiableMap(this.tokens); authorization.attributes = Collections.unmodifiableMap(this.attributes); return authorization; } } }
-
미해결노션사용법 - 조직이 노션을 쓰기 위해 알아야 할 모든 것
네이버 링크
안녕하십니까말씀하신대로https://www.naver.com/ 이렇게 했는데 create embed나 create bookmark 가 자동으로 뜨지 않습니다.
-
미해결
플러터 어뮬레이터 작동 안됩니다.
이렇게 안드로이드 스튜디오 안에서 어뮬레이터를 실행하면 실행에 이상이 없습니다. 이렇게 어뮬레이터만 밖에서 실행하거나 비쥬얼 스튜디오 코드에서 어뮬레이터 실행하면 어뮬레이터가 잘 돌아가지가 않습니다. 구글링 해서 문의했는데 전원 버튼을 누르거나 디바이스 'wipe data'도 했는데 똑같이 어뮬레이터가 실행이 안됩니다. 어떻게 해결해야 할까요?