묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결웹 게임을 만들며 배우는 React
useRef와 useEffect를 사용해서 어떤 props가 달라지는지 확인 부분 질문입니다.
const ref = useRef([]); useEffect(() => { console.log(rowIndex === ref.current[0], cellIndex === ref.current[1], dispatch === ref.current[2], cellData === ref.current[3]); console.log(cellData, ref.current[3]); ref.current = [rowIndex, cellIndex, dispatch, cellData]; }, [rowIndex, cellIndex, dispatch, cellData]);강좌에서 ref.current = [rowIndex, cellIndex, dispatch, cellData]; 부분을 console.log 아래에 두신 이유가 궁금합니다! 또한 useRef를 사용하는 이유가 화면이 리렌더링 되어도 ref에 있는 데이터들은 초기화가 되지 않기 때문이 맞을까요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
slick에 이미지가 안 뜨는 오류
상세이미지에서 전체화면까지는 되었는데, 이미지가 안 떠서 질문드립니다 밑에 이미지 Indicator를 보면 스크롤하면 다음으로 넘어가는 기능은 잘 작동하는 것 같은데이미지 불러오기가 안 되는 것 같습니다만 이유를 모르겠습니다.. 컴파일도 잘 되었구요ㅠ ImagesZoom/index.js 코드 첨부합니다const ImagesZoom = ({ images, onClose }) => { const [currentSlide, setCurrentSlide] = useState(0); return ( <Overlay> <Global /> <Header> <h1>상세 이미지</h1> <CloseBtn onClick={onClose}>X</CloseBtn> </Header> <SlickWrapper> <div> <Slick initialSlide={0} beforeChange={(slide) => setCurrentSlide(slide)} infinite arrows={false} slidesToShow={1} slidesToScroll={1} > {images.map((v) => ( <ImgWrapper key={v.src}> <img src={v.src} alt={v.src} /> </ImgWrapper> ))} </Slick> <Indicator> <div> {currentSlide + 1} {' '} / {images.length} </div> </Indicator> </div> </SlickWrapper> </Overlay> ); }; PostImages.js 코드입니다const PostImages = ({ images }) => { const [showImagesZoom, setShowImagesZoom] = useState(false); const onZoom = useCallback(() => { setShowImagesZoom(true); }, []); const onClose = useCallback(() => { setShowImagesZoom(false); }, []); if (images.length === 1) { return ( <> <img role="presentation" src={images[0].src} alt={images[0].src} onClick={onZoom} /> {showImagesZoom && <ImagesZoom images={images} onClose={onClose} />} </> ); } if (images.length === 2) { return ( <> <div> <img style={{ width: "50%", display: 'inline-block' }} role="presentation" src={images[0].src} alt={images[0].src} onClick={onZoom} /> <img style={{ width: "50%", display: 'inline-block' }} role="presentation" src={images[1].src} alt={images[1].src} onClick={onZoom} /> </div> {showImagesZoom && <ImagesZoom images={images} onClose={onClose} />} </> ); } return ( <> <div> <img style={{ width: "50%" }} role="presentation" src={images[0].src} alt={images[0].src} onClick={onZoom} /> <div role="presentation" style={{ display: 'inline-block', width: '50%', textAlign: 'center', verticalAlign: 'middle' }} onClick={onZoom} > <PlusOutlined /> <br /> {images.length - 1}개의 사진 더보기 </div> </div> {showImagesZoom && <ImagesZoom images={images} onClose={onClose} />} </> ); };
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 제로초님 이미지 업로드 관련 질문이 있습니다.
이미지 업로드를 위한 multer 강의까지 수강을 하였습니다.이미지 업로드를 할때 uploads 폴더에도 이미지가 잘 들어가고UPLOAD_IMAGES_SUCCESS도 잘 나오는 상황입니다.하지만 제로초님의 화면은 제거 버튼과 비록 깨지지만 이미지가 올라간 화면이 보이는데 저는 그 부분이 나오지 않아서 이 점이 궁금하여 질문 드립니다.import React, { useState } from "react"; import { Button, Card, Popover, Avatar, Image, List, Comment } from "antd"; import { RetweetOutlined, HeartOutlined, MessageOutlined, EllipsisOutlined, HeartTwoTone, } from "@ant-design/icons"; import { useCallback } from "react"; import { useSelector, useDispatch } from "react-redux"; import PropTypes from "prop-types"; import PostImages from "./PostImages"; import CommentForm from "./CommentForm"; import PostCardContent from "./PostCardContent"; import { REMOVE_POST_REQUEST, LIKE_POST_REQUEST, UNLIKE_POST_REQUEST, } from "../reducers/post"; import FollowButton from "./FollowButton"; const PostCard = ({ post }) => { //pages/index.js에서 mainPosts에서 하나씩 뜯어서 보내줌 const dispatch = useDispatch(); const [commentFormOpened, setCommentFormOpened] = useState(false); //댓글창 열지 말지 const onLike = useCallback(() => { dispatch({ type: LIKE_POST_REQUEST, data: post.id, }); }, []); //좋아요 const onUnlike = useCallback(() => { dispatch({ type: UNLIKE_POST_REQUEST, data: post.id, }); }, []); //좋아요 취소 const onToggleComment = useCallback(() => { setCommentFormOpened((prev) => !prev); }, []); //폼 버튼 한번 더 누르면 폼 닫기 const onRemovePost = useCallback(() => { return dispatch({ type: REMOVE_POST_REQUEST, data: post.id, }); }, [id]); const id = useSelector((state) => state.user.me?.id); const { removePostloading } = useSelector((state) => state.post); const liked = post.Likers.find((v) => v.id === id); //게시글 좋아요 누른 사람 중에 내가 있는지. return ( <div style={{ marginBottom: 20 }}> <Card cover={post.Images?.[0] && <PostImages images={post.Images} />} //이미지가 존재한다면 PostImages를 출력 actions={[ //카드 아래에 존재하는 것들 <RetweetOutlined key="retweet" />, liked ? ( <HeartTwoTone twoToneColor="red" onClick={onUnlike} /> ) : ( <HeartOutlined key="heart" onClick={onLike} /> ), <MessageOutlined onClick={onToggleComment} key="comment" />, <Popover //더보기 같은 역할 key="more" content={ <Button.Group> {id && post.User.id === id ? ( <> {/* 내가 쓴 글이면 수정, 삭제 */} <Button>수정</Button> <Button type="danger" onClick={onRemovePost} loading={removePostloading} > 삭제 </Button> </> ) : ( // 내가 쓴 글이 아니라면 <Button>신고</Button> )} </Button.Group> } > <EllipsisOutlined /> </Popover>, ]} extra={id && <FollowButton post={post} />} > <Card.Meta //프로필과 내용 등 avatar={<Avatar>{post.User.nickname[0]}</Avatar>} title={post.User.nickname} description={<PostCardContent postData={post.content} />} /> </Card> {commentFormOpened && ( //commentFormOpened가 true이면 열어라 <div> {/* 어떤 게시글에 댓글을 남기는지.. */} <CommentForm post={post} /> <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} //데이터는 여기서 가져와서 renderItem={( item //이런식으로 출력한다 ) => ( <li> <Comment author={item.User.nickname} //댓글쓴사람 avatar={ <Avatar>{item.User.nickname[0]}</Avatar> //아바타 } content={item.content} /> </li> )} /> </div> )} </div> ); }; PostCard.PropTypes = { post: PropTypes.shape({ id: PropTypes.number, User: PropTypes.object, content: PropTypes.string, createdAt: PropTypes.string, Comment: PropTypes.arrayOf(PropTypes.object), Images: PropTypes.arrayOf(PropTypes.object), Likers: PropTypes.arrayOf(PropTypes.object), }).isRequired, }; export default PostCard;저의 PostCard 코드입니다 이 코드에서 cover={post.Images?.[0] && <PostImages images={post.Images} />}이 부분이 이미지들을 출력해주는 부분이 아닌가요?? 저의 화면에는 아래처럼 나오지 않습니다.
-
해결됨웹 게임을 만들며 배우는 React
useRef는 항상 최신 객체를 참조한다?
useRef 역할 중에 "값이 바뀌기는 하지만 화면에 영향주고싶지않을때 사용"이 있잖아요! 이 말이 useRef는 항상 최신 객체를 참조한다는 의미일까요? useRef가 어떻게 항상 최신 객체를 참조할까요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
<npm run dev>시 -61 에러가 나타납니다!
안녕하세요!섹션2 수업내용까지 마치고 npm run dev를 하였는데 오류가 나타납니다 ;-; 커뮤니티에서 -61 에러 관련글을 찾아보니, 데이터베이스 연결 문제를 확인해보라고 하셨는데..;-; 연결 방법을 모르겠습니다..package.json "main"이 index.ts로 되어있어서server.ts로 바꾸었습니다.그랬더니 ;-; 4000번이 열리긴 합니다.그런데 터미널에는 여전히 오류가 함께 나타납니다!!;; 무엇을 확인해보면 될까요 ;-;..?
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
React에서 배열 사용하기 4 - 데이터 수정하기 18분 50초 질문드립니다.
🚨 아래의 가이드라인을 꼭 읽고 질문을 올려주시기 바랍니다 🚨질문 하시기 전에 꼭 확인해주세요- 질문 전 구글에 먼저 검색해보세요 (답변을 기다리는 시간을 아낄 수 있습니다)- 코드에 오타가 없는지 면밀히 체크해보세요 (Date와 Data를 많이 헷갈리십니다)- 이전에 올린 질문에 달린 답변들에 꼭 반응해주세요 (질문에 대한 답변만 받으시고 쌩 가시면 속상해요 😢)질문 하실때 꼭 확인하세요- 제목만 보고도 무슨 문제가 있는지 대충 알 수 있도록 자세한 제목을 정해주세요 (단순 단어 X)- 질문의 배경정보를 제공해주세요 (이 문제가 언제 어떻게 발생했고 어디까지 시도해보셨는지)- 문제를 재현하도록 코드샌드박스나 깃허브 링크로 전달해주세요 (프로젝트 코드에서 문제가 발생할 경우)- 답변이 달렸다면 꼭 확인하고 반응을 남겨주세요- 강의의 몇 분 몇 초 관련 질문인지 알려주세요!- 서로 예의를 지키며 존중하는 문화를 만들어가요. - 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.강사님 안녕하세요 18분 50초에서 DiaryItem.js에서 이 코드 부분 질문드립니다.스크린샷을 봐주시면 제가 임의로 {console.log("진입1")} {console.log("진입2")}를 넣었습니다.이번에는 {console.log("진입1")}을 <> </> 밖으로 뺐습니다.그러자 오류가 생겼습니다.제가 이해하기로는, JSX 표현식인 { } 은 하나의 태그로 묶어줘야 합니다.그런데 저 위치로 console.log("진입1")을 옮겨줘도<div className="content"></div>가 감싸줘서<div className="content">console.log("진입1")</div>인 것과 같아서 JSX 표현식을 태그가 감싸준 것이므로 오류가 안생겨야 할 것 같은데, 생기는 이유에 대해 질문드리고 싶습니다.
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
ApolloError: request to http://mock.com/graphql failed, reason: response3.headers.all is not a function
33-05 강의에서 test에서 자꾸 이 오류가 뜹니다apis.jsimport { graphql } from "msw"; const gql = graphql.link("http://mock.com/graphql") export const apis = [ gql.mutation("createBoard", (req, res, ctx) => { const { writer, title, contents } = req.variables.createBoardInput return res( ctx.data({ createBoard: { _id: "qqq", writer, title, contents, __typepname: "Board", }, }) ); }), // gql.query("fetchBoards", () => {}) ]; jest.setup.jsimport { server } from "./src/commons/mocks/index" beforeAll(() => server.listen()); afterAll(() => server.close()); index.test.tsx import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import StaticRoutingMovedPage from "../../pages/section33/33-05-jest-unit-test-mocking"; import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache, } from "@apollo/client"; import fetch from "cross-fetch"; import mockRouter from "next-router-mock"; jest.mock("next/router", () => require("next-router-mock")); it("게시글이 잘 등록되는지 테스트 하자!", async () => { const client = new ApolloClient({ link: new HttpLink({ uri: "http://mock.com/graphql", fetch, }), cache: new InMemoryCache(), }); render( <ApolloProvider client={client}> <StaticRoutingMovedPage /> </ApolloProvider> ); fireEvent.change(screen.getByRole("input-writer"), { target: { value: "맹구" }, }); fireEvent.change(screen.getByRole("input-title"), { target: { value: "안녕하세요" }, }); fireEvent.change(screen.getByRole("input-contents"), { target: { value: "방가방가" }, }); fireEvent.click(screen.getByRole("submit-button")); await waitFor(() => { expect(mockRouter.asPath).toEqual("/boards/qqq"); }); }); 도와주십쇼 ㅠㅠ
-
미해결[React 1부] 만들고 비교하며 학습하는 React
@ 골뱅이의 역활
안녕하세요!자꾸 질문드려 죄송합니다. 커스텀 이벤트를 호출하실때 계속 @(골뱅이)를 사용하시는데 구글에 아무리 찾아봐도 무슨 역활인지 모르겠습니다. 자바스크립트에 @를 붙이는 경우는 어디에 나와 있나요..? (혹시 제가 강의 내용에 있는 걸 놓친거라면 죄송합니다)
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
React에서 배열 사용하기 2 - 데이터 추가하기 10분 27초 질문드립니다.
🚨 아래의 가이드라인을 꼭 읽고 질문을 올려주시기 바랍니다 🚨질문 하시기 전에 꼭 확인해주세요- 질문 전 구글에 먼저 검색해보세요 (답변을 기다리는 시간을 아낄 수 있습니다)- 코드에 오타가 없는지 면밀히 체크해보세요 (Date와 Data를 많이 헷갈리십니다)- 이전에 올린 질문에 달린 답변들에 꼭 반응해주세요 (질문에 대한 답변만 받으시고 쌩 가시면 속상해요 😢)질문 하실때 꼭 확인하세요- 제목만 보고도 무슨 문제가 있는지 대충 알 수 있도록 자세한 제목을 정해주세요 (단순 단어 X)- 질문의 배경정보를 제공해주세요 (이 문제가 언제 어떻게 발생했고 어디까지 시도해보셨는지)- 문제를 재현하도록 코드샌드박스나 깃허브 링크로 전달해주세요 (프로젝트 코드에서 문제가 발생할 경우)- 답변이 달렸다면 꼭 확인하고 반응을 남겨주세요- 강의의 몇 분 몇 초 관련 질문인지 알려주세요!- 서로 예의를 지키며 존중하는 문화를 만들어가요. - 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.강사님 안녕하세요React에서 배열 사용하기 2 - 데이터 추가하기 10분 27초 코드에 대해 질문드립니다.33라인에서const dataId = useRef(0); 을 주셨는데아래 코드처럼 useRef( ) 를 안쓰고const dataId = 0; 로 썼을 때보다 장점이 있는지 질문드리고 싶습니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
게시글 작성 오류
안녕하세요 제로초님!게시글을 작성하게되면 아래와 같이 성공했다고 응답도 잘 도착하지만 id가 undefined이라고 오류가 납니다.제가 어느 부분을 놓치고 있는건가요??
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
로그인 오류
안녕하세요 제로초님 middleware.js를 사용하여로그인했을때와 하지않았을 때의 경우를 나눠놓으셨자나요!그 강좌를 듣고 코드를 그대로 작성하고 로그인을 진행해보는데올바른 이메일과 비밀번호를 입력해도 이 알림이 뜹니다.. 로그인도 실패로 응답하구요.. 어떤 부분이 문제일 가능성이 높을까요??
-
미해결따라하며 배우는 리액트 네이티브 기초
저는 안드로이드 에뮬레이터가 왜 이렇게 나올까요..?
<SafeAreaView> 써도 이러네요..환경은 윈도우, 안드로이드 스튜디오 pixel4 썼습니다
-
미해결따라하며 배우는 리액트 네이티브 기초
TodoAPP 구조 생성하기에서 스니펫 ??
rnfes 입력했을때 스니펫 관련 익스텐션이 없어선지 자동완성이 안됩니다. 어떤 걸 다운받아야 하나요?
-
미해결처음 만난 리액트(React)
버튼은 뜨는데 눌렀을 때 동작하지 않아요.
17, 17.0.0, 18.0.0 다 해봤는데 버튼은 뜨지만 눌렀을 때 글자가 바뀌지 않아요ㅠ 문제가 무엇일까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
06-01-container-presenter 에러
강의를 잘 따라가면서 작성하고 있었는데 화면을 최종적으로 확인할 때 이런 에러가 뜹니다. 코드를 두번확인했는데 틀린 부분이 보이지않습니다 ..! index.js 코드 입니다import BoardWrite from '../../../src/components/units/board/write/BoardWrite.container' export default function GraphqlMutationPage(){ return ( <> <div>###### 여기는 페이지 입니다 ######</div> {/* //로직을 가져옴 */} <BoardWrite /> <div>###### 여기는 페이지 입니다 ######</div> </> ) }BoardWrite.container.js 코드 입니다import { useMutation } from '@apollo/client' import {useState} from 'react' //UI를 가져옴(파일을 합침) import BoardWriterUI from './BoardWrite.presenter' //gql 가져옴(파일을 합침) import {나의그래프큐엘셋팅} from './BoardWrite.queries' //로직만 사용 export default function BoardWrite() { const [writer, setWriter] = useState() const [title, setTitle] = useState() const [contents, setContents] = useState() const [나의함수] = useMutation(나의그래프큐엘셋팅); const onClickSubmit = async () => { const result = await 나의함수({ variables: { writer: writer, title: title, contents: contents } }) console.log(result) }; const onChangeWriter = (event) => { setWriter(event.target.value) // => state에 저장 } const onChangeTitle = (event) => { setTitle(event.target.value) } const onChangeContents = (event) => { setContents(event.target.value) } return( <> <div>$$$$$$$ 여기는 컨테이너 입니다 $$$$$$</div> {/* UI를 가져옴(파일을 합침) */} <BoardWriterUI aaa={onClickSubmit} bbb={onChangeWriter} ccc={onChangeTitle} ddd={onChangeContents} /> <div>$$$$$$$ 여기는 컨테이너 입니다 $$$$$$</div> </> ) } BoardWrite.presenter.js 코드 입니다import {BlueButton, RedInput } from './BoardWrite.style' //UI만 사용 export default function BoardWriterUI(props){ return( <> <div>@@@@@@@@ 여기는 프리젠터 입니다 @@@@@@@</div> <div> 작성자: <RedInput type="text" onChange={props.bbb} /> 제목: <input type="text" onChange={props.ccc} /> 내용: <input type="text" onChange={props.ddd} /> <BlueButton onClick={props.aaa}>GRAPHQL-API 요청하기</BlueButton> </div> <div>@@@@@@@@ 여기는 프리젠터 입니다 @@@@@@@</div> </> ) }BoardWrite.queries.js 코드 입니다import { gql } from '@apollo/client' export const 나의그래프큐엘셋팅 = gql` mutation createBoard($writer: String, $title: String, $contents: String){ createBoard(writer : $writer , title: $title , contents: $contents) { _id number message } } BoardWrite.style.js 코드 입니다import styled from '@emotion/styled' export const RedInput = styled.input` border-color: red; ` export const BlueButton = styled.button` background-color: blue; ` app.js 코드 입니다// 모든 페이지의 공통설정들 여기서 진행 import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client' export default function App({ Component, pageProps }) { const client = new ApolloClient({ uri:"http://backend-example.codebootcamp.co.kr/graphql", cache: new InMemoryCache() //컴퓨터의 메모리에다가 백엔드에서 받아온 데이터 임시로 저장해 놓기 => 나중에 더 자세히 알아보기 }) return ( <div> <div> ========== 여기는 _app.js 컴포넌트 시작부분 입니다. ==========</div> {/* //그래프큐엘 셋팅 => 앞으로 아래 컴포넌트에서 client를 쓸 수 있다는 의미 */} <ApolloProvider client={client}> <Component /> {/* 내가 들어간 페이지들의 html이 여기로 다 들어오게 됨 */} </ApolloProvider> <div> ========== 여기는 _app.js 컴포넌트 마지막부분 입니다. ==========</div> </div> ) } 마지막으로 폴더 구조입니다
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
section8 포폴 리뷰 에러가 납니다.
boards/new/index.js 에서 항목 기입 후 등록하기 버튼을 누를 시 등록된 상세페이지로 넘어가지 않고 alert 알림창으로 "Response not successful: Received status code 400" 이렇게 뜹니다.오타는 혹시 있나 해서 올려주신 깃허브랑 하나하나 꼼꼼히 비교 했는데 없었어요..오류코드를 개발자도구로 뜯어보니 그래서 해당 부분 확인해봐도 모두 강사님과 동일하게 작성되있는데 뭐가 문제일까요
-
미해결웹 게임을 만들며 배우는 React
가독성을 위한 JSX(XML임!) 수업 질문
안녕하세요 제로초님 , 영상 잘 보고있습니다! 이번 강좌를 보면서 느낀점인데 혹시 XML에 대한 이해도를 갖고 있어야 할까요?HTML만 한 사람의 관점으로 영상을 보았을 때 많이 헷갈리네요아니면 지금은 그냥 이런게 있다라는 느낌일까요??
-
미해결스프링 시큐리티 OAuth2
authenticationEntryPoint 를 기본 설정된 /login이 아닌 react 웹 페이지로 설정 시 cors 문제가 지속해서 발생합니다.
authenticastion Entry Point로 지정 시 cors문제가 계속해서 진행되어 Authorization Code를 받아올 수 없습니다.코드 첨부Authorization Server Config@Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http .getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults() ); http .exceptionHandling((exceptions) -> exceptions .authenticationEntryPoint( new LoginUrlAuthenticationEntryPoint("http://localhost:3000") // new LoginUrlAuthenticationEntryPoint("/login") )) http.oauth2ResourceServer((resourceServer) -> resourceServer .jwt(Customizer.withDefaults())); return http.build(); } @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("oidc-client") .clientSecret("{noop}secret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .redirectUri("https://oidcdebugger.com/debug") .redirectUri("https://test-b821b.web.app") .postLogoutRedirectUri("http://127.0.0.1:8080/") .scope(OidcScopes.OPENID) .scope(OidcScopes.PROFILE) .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) .build(); return new InMemoryRegisteredClientRepository(oidcClient); } @Bean public AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer("http://localhost:6080").build(); } } Default Web Config @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:3000") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .exposedHeaders("*"); } }; } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowCredentials(true); configuration.addAllowedHeader("*"); configuration.addAllowedMethod("*"); configuration.addAllowedOrigin("http://localhost:3000"); configuration.setExposedHeaders(Arrays.asList("*")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .cors(customCorsConfig -> customCorsConfig.configurationSource(corsConfigurationSource())) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer.requestMatchers(AUTH_WHITELIST).permitAll() .anyRequest().authenticated() ) .formLogin( httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer.loginPage("http://localhost:3000").loginProcessingUrl("/login") // Customizer.withDefaults() ) ; return http.build(); } @Bean public UserDetailsService userDetailsService() { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); UserDetails userDetails = User.builder() .username("user") .password(passwordEncoder.encode("password")) .roles("USER") .build(); return new InMemoryUserDetailsManager(userDetails); } @Bean public JWKSource<SecurityContext> jwkSource() { KeyPair keyPair = generateRsaKey(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); RSAKey rsaKey = new RSAKey.Builder(publicKey) .privateKey(privateKey) .keyID(UUID.randomUUID().toString()) .build(); JWKSet jwkSet = new JWKSet(rsaKey); return new ImmutableJWKSet<>(jwkSet); } private static KeyPair generateRsaKey() { KeyPair keyPair; try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); keyPair = keyPairGenerator.generateKeyPair(); } catch (Exception ex) { throw new IllegalStateException(ex); } return keyPair; } @Bean public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) { return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource); } @Bean public PasswordEncoder encoder() { return new BCryptPasswordEncoder(); } 위 코드로 진행했을 시 발생하는 Cors Error입니다 Access to XMLHttpRequest at 'http://localhost:6080/oauth2/authorize?client_id=oidc-client&redirect_uri=https%3A%2F%2Foidcdebugger.com%2Fdebug&scope=openid&response_type=code&response_mode=form_post&state=3it6dl9kewr&nonce=hyq3pnronj7&continue' (redirected from 'http://localhost:6080/login') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.3. 이로 인해 CorsFilter를 적용하게 되면 해당 Cors는 해결되지만 authorization code 가 Redirect되는 시점에 Cors 에러가 새롭게 나옵니다.CorsFilter @Component @Slf4j @Order(Ordered.HIGHEST_PRECEDENCE) public class CorsFilter implements Filter { private static final Set<String> allowedOrigins = new HashSet<String>(); static { allowedOrigins.add( "http://localhost:3000" ); } @Override public void init(FilterConfig fc) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; String origin = request.getHeader(HttpHeaders.ORIGIN); log.info("Origin: {}", origin); if (origin != null) { Optional<String> first = allowedOrigins.stream().filter(origin::startsWith).findFirst(); first.ifPresent(s -> response.setHeader("Access-Control-Allow-Origin", s)); } response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Key, Authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } @Override public void destroy() { } Cors 오류Access to XMLHttpRequest at 'https://oidcdebugger.com/debug?code=1o_jSQQVu6EB4XDq-xCWP3qrzp2VDdGbH_AN7iBJGU5gQRC7BRFkQYdn2A6P-6G5Aoi8XLJLaVR4MSVktNzDvYLMmj_UXahcWfEwQdA-tk4GCw5Bff4mXn2q6mIYfs_d&state=3it6dl9kewr' (redirected from 'http://localhost:6080/login') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.혹시 외부 로그인 페이지와 연동한 예시가 있거나, Cors 문제를 해결할 방안이 있는지 궁금합니다ㅠㅠ
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
Postman Error: read ECONNRESET
이렇게 나옵니다ㅜㅜ 왜 연결이 안되는 걸까요 런스타트는 잘 됩니다. ㅜㅜ
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
특정 페이지의 비로그인 차단
강좌에서처럼 me(내 정보 불러오는 상태변수) 변수를 통해 로그인 유지를 하고특정 페이지에서 비로그인 사용자를 차단할경우useEffect(() => { if(!me){ redirects(); } },[])이런식으로 로직을 짤수가 있는데문제가 브라우저 url로 접속할 경우 초기 me 의 상태가 null 이기 때문에 로그인을 한 상태더라도리다이렉트가 되는 문제가 있었습니다. 물론 ssr을 이용하면 이 문제가 해결은 되지만csr만 이용하는 선에서 리다이렉트가 정상적으로 작동하도록 하고 싶은데 어떻게 하면 좋을까요?