묻고 답해요
156만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
[코드캠프] 부트캠프에서 만든 '완벽한' 프론트엔드 코스 결제 관련 문의드립니다.
현재 강의와 관련없는거 알지만 혹시나해서 문의남깁니다. 현재 강의 구매한 분들 대상으로[코드캠프] 부트캠프에서 만든 '완벽한' 프론트엔드 코스강의 90% 할인해서 구매할 수 있었던 것으로 기억하는데, 이미 기간이 끝난거 알지만 지금이라도 할인된 가격으로 구매할 수 있을까 해서 문의남깁니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
완벽한 프론트엔드
고농축 프론트엔드 강의와 완벽한 프론트엔드 강의 커리큘럼이 차이가 많이 날까요?추가된 강의 공지는 읽어봤는데 기존의 기본적인 리액트 내용에서 업데이트가 많이 되었다던가 등 차이가 많이 나는지 궁금합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
refreshToken 저장의 문제
아래에 질문이 있으나 해결안이 없어 다시 문의드립니다.refreshToken, 쿠키는 정상적으로 전달되고 있습니다.setCookie도 나옵니다. 그러나, Application에 나타나지 않습니다.그리고 조금 기다렸다가 acessToken을 재발급하려고 클릭하면 아래와 같은 메시지가 나타납니다. 크롬에서 써드파티 쿠키를 다 accept하도록 설정이 되어있고, 시크릿 모드에서도 진행해봤고 사파리에서도 동일합니다. 클릭하여 나타난 통신기록을 총 3번입니다.첫번째 통신은 accessToken만료로 발생한 것으로 보입니다.네트워크 통신기록을 살펴보면 UNAUTHENTICATED가 발생된 것을 알수 있습니다. 두번째 통신은 restoreAccessToken의 뮤테이션입니다. 헤더와 페이로드는 아래와 같습니다. jwt가 첨부되지 않았다는 부분입니다. 3번째 통신의 Header와 payload, response입니다. 이 부분은 주어진 조건으로 사용자 정보를 불러오는 query같습니다. 사용자에 관련된 정보가 없이 시도되어 _id null이 표시된것 같습니다.인터넷에 검색해보았지만 잘 모르겠습니다. axios통신으로 refreshToken을 직접 저장해주는 코드를 보긴 했는데, graphql은 설정만 바꾼다고 하셔서 refreshToken을 어떻게 처리하는지 잘 모르겠습니다.관련해서 검색을 통해 https로 환경을 바꾸면 될거다 하여, localhost:3000을 mkcert로 https로 환경을 바꾸려고 해봤는데 설치만 하고 아래의 글을 발견해서 잠깐 멈추었습니다. https://shingy.tistory.com/46 _id null의 문제가 local에 받아온 쿠키를 저장하지 못하는 문제인것 같은데, 어떻게 처리할수있는 방법이 있나 싶어서 여쭤봅니다. 코드 getAccessToken의 코드 import { GraphQLClient, gql } from "graphql-request"; import { IMutation } from "../../../commons/types/generated/types"; const RESTORE_ACCESS_TOKEN = gql` mutation { restoreAccessToken { accessToken } } `; export const getAccessToken = async (): Promise<string | undefined> => { try { const graphQLClient = new GraphQLClient( "https://backend-practice.codebootcamp.co.kr/graphql", { credentials: "include" }, ); const result = await graphQLClient.request<Pick<IMutation, "restoreAccessToken">>( RESTORE_ACCESS_TOKEN, ); const newAccessToken = result.restoreAccessToken.accessToken; return newAccessToken; } catch (error) { if (error instanceof Error) console.log(error); } }; 아폴로 클라이언트 코드 import { ApolloClient, InMemoryCache, ApolloProvider, ApolloLink, fromPromise, } from "@apollo/client"; import { createUploadLink } from "apollo-upload-client"; import { useRecoilState } from "recoil"; import { accessTokenState } from "../../../commons/store"; import { useEffect } from "react"; import { ErrorResponse, onError } from "@apollo/client/link/error"; import { getAccessToken } from "../libraries/getAccessToken"; const GLOBAL_STATE = new InMemoryCache(); interface IApolloSettingProps { children: JSX.Element; } export default function ApolloSetting(props: IApolloSettingProps) { const [accessToken, setAccessToken] = useRecoilState(accessTokenState); useEffect(() => { const result = localStorage.getItem("accessToken"); console.log(result); console.log("현재는 브라우저입니다"); setAccessToken(result ?? ""); }, []); const errorLink = onError(({ graphQLErrors, operation, forward }) => { //1-1.에러캐치 if (typeof graphQLErrors !== "undefined") { for (const err of graphQLErrors) { //1-2.토큰 만료에러인지 체크 if (err.extensions?.code === "UNAUTHENTICATED") { return fromPromise( getAccessToken() .then((newAccessToken) => { setAccessToken(newAccessToken ?? ""); operation.setContext({ headers: { ...operation.getContext().headers, Authorization: `Bearer ${newAccessToken}`, }, }); }) .catch((err) => { console.log("오류입니다"); }), ).flatMap(() => forward(operation)); } } } }); const uploadLink = createUploadLink({ uri: "https://backend-practice.codebootcamp.co.kr/graphql", headers: { Authorization: `Bearer ${accessToken}`, credentials: "include", }, }); const client = new ApolloClient({ link: ApolloLink.from([errorLink, uploadLink]), uri: "http://backend-practice.codebootcamp.co.kr/graphql", cache: GLOBAL_STATE, }); return <ApolloProvider client={client}>{props.children}</ApolloProvider>; }로그인 success의 코드import { gql, useApolloClient, useQuery } from "@apollo/client"; import type { IQuery } from "../../../src/commons/types/generated/types"; import { wrapAsync } from "../../../src/components/commons/libraries/asyncFunt"; const FETCH_USER_LOGGED_IN = gql` query fetchUserLoggedIn { fetchUserLoggedIn { email name } } `; export default function LoginPage() { const client = useApolloClient(); const onClickButton = async (): Promise<void> => { const result = await client.query({ query: FETCH_USER_LOGGED_IN, }); console.log(result); }; return ( <> {/* <div>{data?.fetchUserLoggedIn.name}님, 환영합니다.</div> */} <div>환영</div> <button onClick={wrapAsync(onClickButton)}>클릭하세요</button> </> ); }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
완벽한 프론트엔드 강의와 이 강의 차이점
우연찮게 '완벽한' 프론트엔드 강의를 발견하였는데 제가 지금 듣고 있는 강의와 과정이 많이 다른 걸까요? 앱 개발 관련해서도 코드캠프를 통해 공부하고 싶은데 따로 강의가 있진 않은걸까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새 강의 쿠폰과 관련해서 질문드립니다.
귀찮게 해서 죄송합니다. 혹시 저도 기간을 놓쳐 쿠폰을 사용하지 못했는데 새로운 강의를 수강하기 위한 할인 쿠폰을 받을 수 있을지 문의드립니다. 좋은 강의를 제공해주셔서 감사드립니다 :)
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
포트원 질문입니다
const onClickLoad = async (event: MouseEvent<HTMLDivElement>) => { const IMP = window.IMP; IMP.init("imp00000000"); //실제로는 강의자료에 있는 식별코드 사용했습니다! IMP.request_pay( { pg: "kakaopay", pay_method: "card", name: "아이리버 무선 마우스 외 1개", amount: 10000, buyer_email: "이메일@gmail.com", buyer_name: "홍길동", buyer_tel: "010-4242-4242", buyer_addr: "서울특별시 강남구 신사동", buyer_postcode: "01181", m_redirect_url: "/", }, function (rsp: any) { if (rsp.success) { alert("결제가 성공했습니다."); console.log(rsp); } else { alert("결제에 실패했습니다."); } }, ); };위와 같은 코드로 정상적으로 rsp를 받고, imp_uid 까지 받았습니다. 그런데graphql api 호출을 했더니 _id 가 null이라는 에러가 계속해서 발생하네요. 챗gpt 도움을 받아보니, 백엔드가 impUid를 기반으로 결제 정보를 조회하고 DB에 적립하는 로직이 정상적으로 동작하는지 확인해야한다고 하는데, 에러원인이 무엇이고,어떻게 해결해야 하는지 알려주세요! 포트원 가맹점 식별코드는 강의 자료에 있는 식별코드를 사용했습니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새 강의쿠폰 질문있습니다
저번에 사놓고(2시간들음)다시 공부를 시작하려는데 혹시 새로운 강의로 들을수있을까요! 이번에는 꼭 완강 하겠습니다
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
23-05의 hoc적용시의 렌더링에 대한 질문드립니다.
강의를 너무 잘 듣고 있습니다. 쉽게 설명해주셔서 들으면 이해가 잘되지만, 혼자 코드할때에는 실수나 어림 짐작으로 적용했던 부분이 잘 작동되지 않는데, 이런 저런 시도로도 잘못한 부분을 이해하기 어려워 질문드립니다. 23강의 질문입니다. high-order-function을 진행하면서 hoc로 구분하여 설정하는 경우 렌더링에 대해 질문을 남깁니다. 특히 쿼리에 이름을 주어 작동하는 경우에 대해 질문드립니다. 23-3에서 login-success를 확인하는 쿼리를 제가 짰는데 쿼리에 이름을 주었습니다. 23-3의 login-success화면에서는 오류가 없어서, 해당 코드를 복붙하여 hoc부분에도 적용하였는데 초기 렌더링시에 데이터를 받아오지 못하는 현상이 발생하였습니다. 새로 고침을 해야 데이터가 받아와져서 나타납니다.순서를 붙여 1~4번까지 차례로 진행하였습니다. 23-3 코드의 정상 렌더링 (쿼리 네임을 주어도 정상 렌더링) loginCheck와 mypage로 구분되지 않는 코드에서 graphql에 query를 보내면서 이름을 주었습니다. (hoc가 아닌 전체 로직이 한꺼번에 구성되는 경우 쿼리에 이름을 주는 경우에도 --->정상 렌더링 되었어요. )해당 부분의 쿼리는 아래와 같으며 JWT로서 데이터를 받아와 사용자명이 렌더링 되었습니다.const FETCH_USER_LOGGED_IN = gql` query fetchUserLoggedIn{ fetchUserLoggedIn { email name } } `; 23-5부분에 hoc를 이용하여 loginCheck를 분리한 경우(이 부분은 1번의 (23-3의 코드를 복붙하여 mypage와 logincheck로 분리하였습니다. 오류가 없어서 굳이 수정하지 않아도될것 같아 그냥 두었던 부분입니다) 그런데 사용자명을 받아오지 못한채로 화면이 렌더링이 되었습니다. (graphql이 실패한 것으론 나타납니다. 네트워크를 보면 쿼리를 보내면서 토큰을 첨부하지 못해서 bearer뒷 부분이 없었습니다.) 다만 새로고침을 하면 정상 렌더링이 됩니다. MyPage 코드는 아래와 같습니다. import { gql, useQuery } from "@apollo/client"; import type { IQuery } from "../../../src/commons/types/generated/types"; import { loginCheck } from "../../../src/components/commons/hocs/loginCheck"; const FETCH_USER_LOGGED_IN = gql` query fetchUserLoggedIn{ fetchUserLoggedIn { email name } } `; function MyPage() { const { data } = useQuery<Pick<IQuery, "fetchUserLoggedIn">>(FETCH_USER_LOGGED_IN); return <>{data?.fetchUserLoggedIn.name}님, 환영합니다.</>; } export default loginCheck(MyPage); loginCheck는 아래와 같습니다.import { useEffect } from "react"; import { useRouter } from "next/router"; export const loginCheck = (컴포넌트: any) => (프롭스: any) => { const router = useRouter(); useEffect(() => { if (localStorage.getItem("accessToken") === null) { alert("로그인 후 이용가능합니다"); void router.push("/section23/23-05-login-check-hoc"); } }, []); //if return <컴포넌트 {...프롭스} />; }; hoc를 이용하여 loginCheck를 분리한 경우에 작동원리에 대해 여러가지 검색을 하고, 그 결과를 참조하여 loginCheck에서 accessToken을 받아오고, 만약에 토큰이 없으면 myPage를 렌더링하지 않도록 설정했더니 쿼리네임이 있는 경우에도 정상렌더링이 되었습니다.import { useEffect } from "react"; import { useRouter } from "next/router"; export const loginCheck = (컴포넌트: any) => (프롭스: any) => { const router = useRouter(); const accessToken = localStorage.getItem("accessToken"); useEffect(() => { if (localStorage.getItem("accessToken") === null) { alert("로그인 후 이용가능합니다"); void router.push("/section23/23-05-login-check-hoc"); } }, []); if(accessToken == null ) return null; return <컴포넌트 {...프롭스} />; }; 그리고 더불어, 원두님이 하신것처럼 이름이 없는 쿼리로 설정하였을 때는 loginCheck에 토큰 관련 설정을 넣지 않아도 페이지는 정상 렌더링 되었습니다. (이 부분은 수업에서 진행하신 것입니다)const FETCH_USER_LOGGED_IN = gql` query { fetchUserLoggedIn { email name } } `; 인공지능 모델들은 쿼리에 이름이 있는 경우는 cash에 내용을 참조하여 Header에 토큰이 없는 상태의 설정대로 query를 보내고 받아온다고 설명합니다. 그리고 원두님처럼 쿼리명을 주지 않으면 무명쿼리로서 새로 토큰을 붙이고 쿼리를 보내는 과정을 수행한다고 합니다. 그러나 제가 항상 토큰을 지우고 새로 고침한후에 로그인 부분을 확인하므로 기존의 쿼리를 받은 기록이 캐시에 남아서 영향을 미칠것 같지는 않습니다. 그리고, hoc가 아닌 기존의 23-3은 잘 작동했으니, 이 부분의 통신에 대해서 어떤 부분인가 제가 잘 이해하지 못하는 부분이 있는 것 같습니다. 이후 hoc함수를 사용할때에도 유의해야 할것 같아 왜 이러한 현상이 나타나는 것인지에 대해 조언해 주시면 추가적으로 검색을 통해 이해고자 합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
node.js 설치 질문
문제1 vcs내에 cmd에서는 node -v, npm -v의 버전이 잘 나오는데 bit bash 환경에서는 사진과 같이 오류가 나옵니다 어러번 node.js를 삭제하고 깔아봤는데 계속 이럽니다문제2git bash환경에서 오류가 떠 cmd에서 시도를 해보았습니다.명령프롬프트(cmd)에서는 node, npm, yarn의 버전이 잘 나오지만vsc내에 cmd에서는 나머지의 버전은 잘 나오지만 yarn의 버전이 나오질 않습니다.버전을 모두 확인 후 npx create-next-app를 해야하는데 앞의 문제들 때문에 그런지 npx create-next-app가 실행이 되지 않습니다.초반 세팅 과정 때문에 시간을 너무 잡아먹는 것 같아요 도와주세요 ㅠㅠ
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새 강의 쿠폰 질문있습니다.
안녕하세요 강의를 너무 잘 듣고 있습니다. 강의가 업데이트가 되었다는 공지를 오늘 처음 확인을 하게 되었는데 업데이트 된 내용으로 새로 듣고 싶은데 쿠폰을 혹시 발급 받을 수 있을까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
기존 강의 자료 관련 주소, 새 강의 쿠폰
graphql 연습 관련 주소가 더 이상 유지되고 있지 않는 것 같습니다. 일단은 새 강의 쿠폰 관련 문의 드렸는데, 이전 강의에 해당하는 주소들은 이제 유지하지 않는 건가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새 강의 쿠폰 관련 문의
안녕하세요!! 강의 너무 잘 듣고 있습니다! 다름이 아니라 css 거의 마지막까지 왔는데 새 강의가 나왔다는 사실을 알게 되어서저도 새로운 버전으로 강의를 수강하고 싶은데요! ㅠㅠ실제로 82강에 나오는 my-shop 깃헙 페이지가 다운되기도 했고 해서 새로운 버전으로 꼭 수강하고 싶습니다!!저도 쿠폰을 받을 수 있을까요?좋은 강의 제공해 주셔서 정말 감사합니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새강의 쿠폰 발급 관련 질문
저도 이전에 미리 구매해놓고 공부하려는데 뒤늦게 확인했습니다.리뉴얼된 버전으로 공부하고 싶은데, 쿠폰을 발급받을 수 있을까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
타입에러가 계속생기는데
타입 포함된 버전을 설치한 후에도 타입에러가 계속 생기는데버전이슈일까요.."dependencies": { "@ant-design/icons": "^5.5.2", "@apollo/client": "^3.11.9", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "antd": "^5.21.6", "axios": "^1.7.7", "graphql": "^16.9.0", "lodash": "^4.17.21", "next": "12.1.0", "react": "17.0.2", "react-daum-postcode": "^3.2.0", "react-dom": "17.0.2", "react-infinite-scroller": "^1.2.6", "ts-node": "^10.9.2" }, "devDependencies": { "@eslint/js": "^9.18.0", "@graphql-codegen/cli": "^5.0.3", "@graphql-codegen/typescript": "^4.1.2", "@types/node": "17.0.2", "@types/react": "17.0.2", "@types/react-infinite-scroller": "^1.2.5", "eslint": "^9.18.0", "eslint-config-next": "15.1.4", "eslint-config-prettier": "^10.0.1", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.4", "globals": "^15.14.0", "prettier": "^3.4.2", "typescript": "^5.7.3", "typescript-eslint": "^8.20.0""react-infinite-scroller": "^1.2.6", 이고"@types/react-infinite-scroller": "^1.2.5", 로 되어있습니다.++ return ( <div style={{ height: '700px', overflow: 'auto' }}> <InfiniteScroll pageStart={0} loadMore={onLoadMore} hasMore={hasMorePosts} loader={ <div className='loader' key='loader'> Loading... </div> } useWindow={false} > {data?.fetchBoards?.map((el) => el ? ( <div key={el._id}> <span style={{ margin: '10px' }}>{el.title}</span> <span style={{ margin: '10px' }}>{el.writer}</span> </div> ) : null, ) ?? []} </InfiniteScroll> </div> );1. InfiniteScroll 'InfiniteScroll' cannot be used as a JSX component. Its instance type 'InfiniteScroll' is not a valid JSX element. Property 'refs' is missing in type 'InfiniteScroll' but required in type 'ElementClass' 3. loader Type 'Element' is not assignable to type ReactElement<unknown, string | JSXElementConstructor<any>> Types of property 'key' are incompatible. TYpe 'Key | null' is not assignable to type 'string | null'. Type 'number' is not assignable to type 'string'. 4. {data?.~</div>;})} Type 'void[] | undefined' is not assignable to type 'ReactNode'.2. {data?.~</div>;})}Type 'void[] | undefined' is not assignable to type 'ReactNode'. (이하 생략)이런 에러였었는데,상단에 타입선언 해주고 모두 해결 되었습니다.import React, { ReactElement } from 'react'; declare module 'react-infinite-scroller' { interface InfiniteScrollProps { pageStart?: number; loadMore: () => void; hasMore?: boolean; loader?: ReactElement | null; useWindow?: boolean; children?: React.ReactNode; } export default function InfiniteScroll(props: InfiniteScrollProps): ReactElement; }모듈이 덜 설치된건지 ㅠㅠ 왜 이런 에러가 생긴지 모르겠네요 ㅠㅠ
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
강의 관련 질문드립니다.
24년 12월에 결제하여 수강 중입니다올해 [코드캠프] 부트캠프에서 만든 '완벽한' 프론트엔드 코스 강의가 오픈 된 것을 보았는데, 강의 간 내용에 많은 차이 점이 있을까요? 신규 강의를 수강하려면 재결제 하는 방법밖에 없을까요..?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새 강의 쿠폰 관련 문의드립니다
안녕하세요! 비슷한 유형의 질문글을 보고 늦었지만 문의드리게 되었습니다. 공지를 늦게 확인하여 쿠폰을 받지 못했는데 혹시 지금이라도 발급이 가능할까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
안녕하세요,
4월에 구매하여 늦게서야 해당 강의를 수강 중인데 업데이트 된 강의 쿠폰 발급하는걸 몰랐네요ㅠㅠ 혹시 현강의랑 많이 다를까요? 어떤 차이점이 있는지 궁금해요.듣는다고 하면 중간부터 들어야하나요. 현재 eslint부분 듣고있는데 애매하네요. 그리고 듣는다는 가정 하에 쿠폰 재발급이 가능한지 궁금합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
eslint와 prettier
eslint를 설치하면 이전과 달리 esling.config.mjs로 파일이 설정되며 extend나 rules의 활용이 기존과 다른 방식으로 진행되며 충돌이 일어납니다. stackoverflow나 chatGPT의 도움을 받아 동작은 되지만, 이 부분 다시 설명해주셔야 하는게 아닌지요. (기존에 설명된 부분들도 코드나 적용방식이 달라져서, 플러그인을 활용하도록 합니다)이 부분을 스스로 혼자 찾아서 수정할 수 있는 역량이 있다면 이 강의를 듣지 않겠지요. 하루 종일 한단원도 나가지 못하고 .. 해결하려다 포기하게 되네요. 2년전에 산 강의를 시간이 없어 이제 듣는 제 문제도 있겠지만.. 이 부분 상세한 설명을 넣어주셔야 할것 같아요.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
새 강의 쿠폰 관련 문의드립니다!
아랫분들처럼 강의를 사두고 이제야 수강하다가 쿠폰 관련한 공지를 늦게 보게 되었는데요.혹시 아직 쿠폰 발급이 가능할까요?
-
미해결Next + React Query로 SNS 서비스 만들기
next.js 13에 dynamic import 적용 시 SEO 영향
안녕하세요, 아래와 같은 방식으로 dynamic import 적용하려는데 SEO에 부정적인 영향이 가는지 궁금해서 질문드립니다.조금 찾아보니 Dynamic Import로 모두 제외해 버렸기 때문에 Google Bot 등의 크롤러가 웹 페이지에 방문했을 때 접속한 document 페이지에 크롤링할만한 정보가 없게 될 것이라는 이야기가 있어서 사실인지 궁금합니다.사실이라면 dynamic import의 해당 단점을 보완할 수 있는 방법으로 ssr:true 설정 이외에 무엇이 있는지 궁금합니다 import dynamic from "next/dynamic"; const Zerocho = dynamic(() => import("features/Zero/components/templates/Zerocho"), { ssr: false }); export default function Hi() { const seo = getPageSeo("hihi"); return ( <> <NextSeo {...seo} /> <SchemaScript schema={schema} /> <Head> <meta name="hello" content={seo.hh} /> </Head> <Title>{seo.title}</Title> <Body> <Zerocho /> </Body> </> ); } export const getServerSideProps = async (context) => { return { props: { mode: true, modeCont: false, ...(await serverSideTranslations(context.gggg, ["hihi", "dodo"])), }, }; };