묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
빌드 오류(babel-eslint -> @babel/eslint-parser)
안녕하세요 강사님. 명쾌한 강의 덕분에 많이 배우고 있습니다. 지금까지는 모르는 부분이나 오류를 직접 찾아보고 해결해왔는데 빌드 부분은 도저히 못 찾겠어서 질문글을 작성하였습니다ㅜㅜ... 수강 도중 빌드에 계속 오류가 생겨 질문 남깁니다.(강사님의 초기설정 .eslintrc.js 파일을 사용시 오류) 초반에 강사님의 깃에서 초기설정 버전 파일을 클론해서 시작할 때부터 ts, tsx 파일 맨 첫줄들이 다 빨간줄이 떴었는데 npm run dev는 실행되길래 무시해도 되는 오류인 줄 알고 진행했습니다.ㅜㅜ .. 빌드가 안되는걸 이제서야 알았어요...babel-eslint 가 @babel/eslint-parser로 버전이 바뀌어 오류가 난다 해서 변경시도도 해보았는데 node_modules에서 못찾는다는 오류가 발생해서 지금은 다시 babel-eslint로 커밋한 시점에 돌려놓은 상태입니다.원인이 너무 궁금한데 이틀째 아침부터 저녁까지 해결방법만 찾고있는데도 해결을 못했습니다ㅜㅜ...부끄럽지만 해결 방법에 대해 질문 남깁니다..eslintrc.js 파일에서 parser : '@typescript-eslint/parser'과 parser:'babel-eslint'를 같이 쓰면 안되는 것 같지만 둘 중 하나만 지워도 또 에러가 떠서 저 상태를 유지하고 있습니다. 깃 주소는 해결되어 지우겠습니다긴 글 읽어주셔서 감사합니다.> 현재 오류 //.eslintrc.js 파일 module.exports = { parser: '@typescript-eslint/parser', // Specifies the ESLint parser extends: [ 'airbnb', 'plugin:@typescript-eslint/recommended', 'plugin:import/typescript', 'plugin:prettier/recommended', 'babel-eslint', 'prettier', ], plugins: ['babel-eslint', '@typescript-eslint', 'prettier', 'react-hooks'], parserOptions: { ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features sourceType: 'module', // Allows for the use of imports ecmaFeatures: { jsx: true, }, project: './tsconfig.json', }, parser: 'babel-eslint', env: { browser: true, jest: true, es6: true, node: true, }, globals: { cy: true, Cypress: true, }, rules: { 'arrow-parens': [2, 'always'], '@typescript-eslint/no-unused-vars': [ 1, { argsIgnorePattern: 'res|next|stage|^err|on|config|e', }, ], 'arrow-body-style': ['error', 'as-needed', { requireReturnForObjectLiteral: false }], 'no-param-reassign': [ 2, { props: false, }, ], 'no-unused-expressions': [ 1, { allowTaggedTemplates: true, }, ], '@typescript-eslint/prefer-interface': 0, '@typescript-eslint/explicit-function-return-type': 0, '@typescript-eslint/no-use-before-define': 0, '@typescript-eslint/camelcase': 0, '@typescript-eslint/no-var-requires': 0, '@typescript-eslint/no-explicit-any': 0, '@typescript-eslint/no-non-null-assertion': 0, 'no-console': 0, 'spaced-comment': 0, 'no-use-before-define': 0, 'linebreak-style': 0, 'consistent-return': 0, import: 0, 'func-names': 0, 'import/no-extraneous-dependencies': 0, 'import/prefer-default-export': 0, 'import/no-cycle': 0, 'import/extensions': 0, 'import/no-unresolved': 0, 'space-before-function-paren': 0, 'react/jsx-one-expression-per-line': 0, 'react/no-danger': 0, 'react/display-name': 1, 'react/react-in-jsx-scope': 0, 'react/jsx-uses-react': 1, 'react/forbid-prop-types': 0, 'react/no-unescaped-entities': 0, 'react/prop-types': 0, 'react/jsx-filename-extension': [ 1, { extensions: ['.js', '.jsx', '.tsx'], }, ], 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', quotes: [ 2, 'single', { avoidEscape: true, }, ], 'prettier/prettier': 'error', 'jsx-a11y/href-no-hash': 'off', 'jsx-a11y/anchor-is-valid': [ 'warn', { aspects: ['invalidHref'], }, ], }, }; // package.json 파일 { "name": "blahx2", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@chakra-ui/icons": "^2.0.17", "@chakra-ui/react": "^1.8.8", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "axios": "^1.2.3", "chrome-aws-lambda": "^6.0.0", "firebase": "^9.15.0", "firebase-admin": "^11.4.1", "framer-motion": "^6.5.1", "moment": "^2.29.4", "next": "12.0.7", "playwright-core": "^1.30.0", "react": "17.0.2", "react-dom": "17.0.2", "react-query": "^3.39.3", "react-textarea-autosize": "^8.4.0", "styled-components": "^5.3.3" }, "devDependencies": { "@types/node": "^16.11.13", "@types/react": "^17.0.37", "@types/styled-components": "^5.1.18", "@typescript-eslint/eslint-plugin": "^5.8.0", "@typescript-eslint/parser": "^5.8.0", "babel-eslint": "^10.1.0", "babel-plugin-styled-components": "^2.0.2", "eslint": "8.4.1", "eslint-config-airbnb": "^19.0.2", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^16.1.0", "eslint-config-next": "12.0.7", "eslint-config-prettier": "^8.3.0", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-import": "^2.25.3", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.27.1", "eslint-plugin-react-hooks": "^4.3.0", "prettier": "^2.5.1", "typescript": "^4.5.4" } }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
yarn generate 에러
Invalid Custom Plugin "typescript" 라면서 에러가 뜹니다 어떻게 해결해야 할까요?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
커리큘럼 질문
안녕하세요 ! 이번에 리액트 강의를 구매했는데 커리큘럼 문제로 질문드립니다.리액트 강의를 듣기전에따라하며 배우는 노드 ,리액트 시리즈 기본강의=>유튜브=>영화사이트 =>챗봇 =>쇼핑몰 =>리액트 A-Z=>레딧사이트 만들기 이순서로 들을까 하는데 괜찮을까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
쿠키파서 기능
쿠키파서가 없어도 쿠키가 보내지고 데이터를 받을 때 원본이 오는데 쿠키파서 미들웨어가 필수인건가요?
-
해결됨따라하며 배우는 리액트 A-Z[19버전 반영]
마이너스 버튼 관련 질문
안녕하세요아래와 같이 코드를 작성할 경우, 0 으로 입력하면 에러 메세지가 보여집니다.반대로 플러스 버튼과 동일하게 1로 하면 에러 없이 제대로 작동이 됩니다.왜 0으로 하면 에러가 발생하는 걸까요?test('when the - button is pressed, the counter change to 0', () => { render(<App />); const buttonElement = screen.getByTestId("minus-button"); // click plus button fireEvent.click(buttonElement); // 카운터가 1에서 -1로 되서 0이 된다. const counterElement = screen.getByTestId("counter"); expect(counterElement).toHaveTextContent(0); })
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
index.js 오류
post/user/등 강좌 되돌려 보면서 틀린 곳이 있나 확인했는데,어디서 부터 고쳐야 될지 모르겠습니다. index.jsimport React from 'react'; import { useSelector } from 'react-redux'; import PostForm from '../components/PostForm'; import PostCard from '../components/PostCard'; import AppLayout from '../components/AppLayout' const Home = () => { const { isLoggedIn } = useSelector((state) => state.user); const { mainPosts } = useSelector((state) => state.post); return ( <AppLayout> {isLoggedIn && <PostForm/>} {mainPosts.map((post)=><PostCard key={post.id} post={post}/>)} </AppLayout> ); } export default Home;
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
8:24분경 코드 오류나서 수정하셨는데!
8분 20초경 오류나서 코드를 수정하셨는데,case ADD_POST_TO_ME: return { ...state, me: { ...state.me, // 이부분 왜 추가한건가요? Posts: [{ id: action.data }, ...state.me.Posts], }, };코드 주석부분 왜 추가하신건가요..?const dummyUser = (data) => ({ ...data, nickname: "wewewe", id: 1, Posts: [{ id: 1 }], Followings: [{ nickname: "we1" }, { nickname: "we2" }, { nickname: "we3" }], Followers: [{ nickname: "we1" }, { nickname: "we2" }, { nickname: "we3" }], });dummy데이터 구조를 보면Posts를 제외한 nickname, id, Followings 등 변하지 않는 데이터들의 불변성을 유지하기 위해서 추가한것이 맞는건가요?!
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
ADD_COMMENT_SUCCESS 새로운 post에서만 작동 안 하는 문제
안녕하세요 제로초 님,redux saga immer, faker 까지만 들었구요, 백엔드는 안 만들었고요.faker가 문제가 있다길래,import { faker } from '@faker-js/faker';이거를 대신해서 설치해서 했는데, 이거의 문제는 아닌거 같고요. ADD_COMMENT_SUCCESS가기존의 post에서는 작동을 하는데, 새로운 post에서만 작동 안 하는 문제가 있습니다. 새로운 post.id가 동작을 안 하나 싶어const post = draft.mainPosts.find((post)=> { console.log(post.id == action.data.postId); return post.id == action.data.postId; });를 해봤는데 역시 true가 나옵니다.아래는 브라우저 결과 사진들 입니다.로그인 안 했을 때,로그인 하고 기존의 post에 comment를 달았을 때 -> 정상적으로 동작 함. 로그인 하고 새로운 post을 하나 추가 -> 정상적으로 동작 함. username이 바뀌는 건 faker를 썼고, userId만 그래도 컴포넌트에서 받아서 써서 지금 ADD_POST_TO_ME 동작해서 1에서 2로 바뀌었구요.그러나새로운 post에 comment를 달았을 때 -> 콘솔로그가 true가 나옴에도 동작을 하지 않음. 기존의 post에도 comment가 안 달리는 거면 제가 실수를 한게 분명한데, 새로운 post에만 동작을 안 하는게 이상합니다.ADD_COMMENT_REQUEST를 보냈을 때에도, 갑자기 mainPosts가 초기화 되거나 그런 것도 아니었습니다. 만약 그랬다면 콘솔로그가 false가 나왔어야 합니다. 감사합니다!
-
해결됨만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
강의 관련 질문입니다!
members.add.ts 25번째 줄에 return이 없어도 괜찮은건가요?또 개인적인 생각으로는 api를 호출하는 부분에서 axios를 사용하면 코드가 더 간단해질 것 같은데 fetch가 사용된 이유가 있을까요?바쁘실텐데 여러 사소한 질문들 드려서 죄송합니다.
-
해결됨만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
useState의 set 함수에 대해서 질문이 있습니다.
useState의 set 함수가 비동기로 실행된다고 이해하고 있는데setLoading(true); setAuthUser({ uid: authState.uid, email: authState.email, photoURL: authState.photoURL, displayName: authState.displayName, }); setLoading(false);이 부분의 코드가 어떻게 순서대로 실행이 되는건가요? set 함수끼리는 순서가 지켜지나요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
VsCode : Import하지 않은 컴포넌트 경고표시X(설정?)
현재 버전 정보들입니다 "next": "^11.1.4", "prop-types": "^15.8.1", "eslint": "^8.33.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0"TypeScript와 React를 사용해서 프로젝트 했을 때는 VsCode에서(웹스톰만의 기능은 아니라고 생각합니다.) import하지 않은 컴포넌트에 대한 경고문이 나와서, 맥북 기준 커맨드+.을 하면 Code Action으로 import을 시켜줄 수 있었습니다.그런데강의를 진행하면서 컴포넌트를 import하지 않은 상황인데도 불구하고, 따로 경고문이 나타나지 않는데, 이게 어떤 설정을 해야하는건지 잘 모르겠습니다.<Menu /> , <Col /> , <UserProfile /> 같은 컴포넌트들입니다.(코드는 이 정도만 첨부하겠습니다.)import PropTypes from "prop-types"; import Link from "next/link"; import { Menu, Input, Row, Col } from "antd"; ... <Row gutter={8}> <Col xs={24} md={6}> {isLoggedIn ? <UserProfile /> : <LoginForm />} </Col> <Col xs={24} md={12}> {children} </Col> <Col xs={24} md={6}> <a href="https://velog.io/" target="_blank" rel="noreferrer noopener">Velog</a> </Col> </Row> </div> ); }; AppLayout.propTypes = { children: PropTypes.node.isRequired, }; export default AppLayout;Import되지 않은 컴포넌트인 <UserProfile />, <LoginForm /> 경고문이 뜨지 않는 사진도 첨부했습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
수정 페이지 마운트 직후, 온체인지 핸들러가 인풋 값을 ""으로 인식하여, 인풋 값를 지웠을 경우, 온체인지 핸들러가 값의 변동을 인식하지 못하는 문제가 있습니다.
안녕하세요 강의 잘 보고있습니다.수정 페이지 마운트 직후, 온체인지 핸들러가 인풋 값을 ""으로 인식하고, 인풋 값를 지웠을 경우, 온체인지 핸들러가 값의 변동을 인식하지 못하는 문제가 있습니다.즉, 수정 페이지의 각 인풋 디폴트 값들에 데이터가 불러와진 후, 값을 입력하는 것이 아니라 삭제할 경우에 값의 변동을 온체인지 핸들러가 인식하지 못하여 발생합니다.따라서, 인풋의 온체인지 핸들러를 통해 인풋 값들이 비어있는지 확인하는 유효성 검사에 작은 오류가 있습니다. 수정페이지 마운트 직후 데이터를 불러온 뒤, 값이 인풋에 입력된 후, 온체인지핸들러가 변동을 인식하게 할 수 있을까요? 혹시 비슷한 인풋 값들을 객체에 묶어 저장하는 방식을 지양해야 할까요? import { type ChangeEvent, useState, useRef, useEffect } from 'react'; import { useRouter } from 'next/router'; import BoardWrite_presenter from './BoardWrite_presenter'; import { CREATE_BOARD, UPDATE_BOARD, UPLOAD_FILE } from './BoardWrite_queries'; import { useMutation, useQuery } from '@apollo/client'; import { type IBoardWrite_container_Props, type IUpdatedVariables, } from './BoardWrite_types'; import { type Address } from 'react-daum-postcode/lib/loadPostcode'; import { checkFileValidation } from '../../commons/checkFileValidation'; import { Modal, Spin } from 'antd'; import type { RcFile } from 'antd/es/upload'; import type { UploadFile } from 'antd/es/upload/interface'; import { getBase64 } from '@/src/commons/utils/utils'; import { FETCH_BOARD } from '../detail/BoardDetail_queries'; import { type IQuery } from '@/src/commons/types/generated/types'; import { Loading3QuartersOutlined } from '@ant-design/icons'; export default function BoardWrite_container( props: IBoardWrite_container_Props ) { const router = useRouter(); const boardId = router.query.boardId; if (props.isEditing && !boardId) { return ( <Spin indicator={<Loading3QuartersOutlined spin />} /> ) } const { data } = useQuery<Pick<IQuery, 'fetchBoard'>>(FETCH_BOARD, { variables: { boardId, }, }); const fetchBoard = data?.fetchBoard; if (props.isEditing && !boardId && !fetchBoard) { return ( <Spin indicator={<Loading3QuartersOutlined spin />} /> ) } const [createBoard] = useMutation(CREATE_BOARD); const [updateBoard] = useMutation(UPDATE_BOARD); const [images, setImages] = useState('youtube'); const [isOpen, setIsOpen] = useState(false); const [writerError, setWriterError] = useState(false); const [passwordError, setPasswordError] = useState(false); const [titleError, setTitleError] = useState(false); const [contentsError, setContentsError] = useState(false); const [valid, setValid] = useState(false); interface ICoreInput { [key: string]: string writer: string password: string title: string contents: string } const [coreInput, setCoreInput] = useState<ICoreInput>({ writer: fetchBoard?.writer ?? '', password: '', title: fetchBoard?.title ?? '', contents: fetchBoard?.contents ?? '' }) const [coreInputErorr, setCoreInputErorr] = useState({ writer: false, password: false, title: false, contents: false }) useEffect(() => { if (fetchBoard) { setCoreInput({ ...coreInput, writer: String(fetchBoard.writer), title: String(fetchBoard.title), contents: String(fetchBoard.contents), }) } }, [data]) const onChangeCoreInput = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { setCoreInput({ ...coreInput, [e.currentTarget.id]: e.currentTarget.value }); setCoreInputErorr({ ...coreInputErorr, [e.currentTarget.id]: false }); const AllInputs: string[] = []; for (const prop in coreInput) { if (prop !== e.currentTarget.id) { AllInputs.push(coreInput[prop]) } else { AllInputs.push(e.currentTarget.value) } } console.log("AllInputs : " + AllInputs) if (!AllInputs.includes('' && "undefined")) { setValid(true); } else setValid(false); }; const onChangeImages = (e: ChangeEvent<HTMLInputElement>) => { setImages(e.target.value); }; const [input, setInput] = useState({ zipcode: "", address: "", addressDetail: "", youtubeUrl: "" }) const onChangeInput = (e: ChangeEvent<HTMLInputElement>) => { setInput({ ...input, [e.target.id]: e.target.value }); }; const onSubmit = async (e: { preventDefault: () => void }) => { e.preventDefault(); if (coreInput.writer === '') { setWriterError(true); } if (coreInput.password === '') { setPasswordError(true); } if (coreInput.title === '') { setTitleError(true); } if (coreInput.contents === '') { setContentsError(true); } if (coreInput.writer && coreInput.password && coreInput.title && coreInput.contents) { try { const result = await createBoard({ variables: { createBoardInput: { writer: coreInput.writer, contents: coreInput.contents, password: coreInput.password, title: coreInput.title, youtubeUrl: input.youtubeUrl, images, boardAddress: { zipcode: input.zipcode, address: input.address, addressDetail: input.addressDetail, }, }, }, }); void router.push(`/boards/${result.data.createBoard._id}`); } catch (error) { if (error instanceof Error) alert(error.message); } } }; const onUpdate = async (e: { preventDefault: () => void }) => { e.preventDefault(); const updatedVariables: IUpdatedVariables = { boardId, password: coreInput.password, updateBoardInput: { contents: coreInput.contents, title: coreInput.title, youtubeUrl: input.youtubeUrl, images, boardAddress: { zipcode: input.zipcode, address: input.address, addressDetail: input.addressDetail, }, }, }; if (!coreInput.password) { setPasswordError(true); } if (!coreInput.title) { setTitleError(true); } if (!coreInput.contents) { setContentsError(true); } if (!coreInput.password && !coreInput.title && !coreInput.contents) { try { const result = await updateBoard({ variables: updatedVariables, }); void router.push(`/boards/${result.data.updateBoard._id}`); } catch (error) { if (error instanceof Error) alert(error.message); } } }; const onToggleModal = () => { setIsOpen((prev) => !prev); }; const handleComplete = (data: Address) => { let fullAddress = data.address; let extraAddress = ''; if (data.addressType === 'R') { if (data.bname !== '') { extraAddress += data.bname; } if (data.buildingName !== '') { extraAddress += extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName; } fullAddress += extraAddress !== '' ? ` (${extraAddress})` : ''; } setInput({ ...input, address: fullAddress, zipcode: data.zonecode }); onToggleModal(); }; const [uploadFile] = useMutation(UPLOAD_FILE); const [imgUrl, setImgUrl] = useState("") const fileRef = useRef<HTMLInputElement>(null) const onClickFile = () => { fileRef.current?.click() } const onChangeFile = async (e: ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; const isValid = checkFileValidation(file); if (!isValid) { return } if (isValid) { try { const result = await uploadFile({ variables: { file } }) setImgUrl(result.data?.uploadFile.url) } catch (error) { if (error instanceof Error) { Modal.error({ content: error.message }) } } } } const [previewOpen, setPreviewOpen] = useState(false); const [previewImage, setPreviewImage] = useState(''); const [previewTitle, setPreviewTitle] = useState(''); const [fileList, setFileList] = useState<UploadFile[]>([]); const handleCancel = () => { setPreviewOpen(false); }; const handlePreview = async (file: UploadFile) => { if (!file.url && !file.preview) { file.preview = await getBase64(file.originFileObj as RcFile); } setPreviewImage(file.url ?? (file.preview as string)); setPreviewOpen(true); setPreviewTitle(file.url ? (file.name || file.url.substring(file.url.lastIndexOf('/') + 1)) : ""); }; return ( <BoardWrite_presenter onChangeInput={onChangeInput} onChangeCoreInput={onChangeCoreInput} onChangeImages={onChangeImages} writerError={writerError} passwordError={passwordError} titleError={titleError} contentsError={contentsError} zipcode={input.zipcode} address={input.address} onSubmit={onSubmit} onUpdate={onUpdate} valid={valid} isEditing={props.isEditing} data={data} isOpen={isOpen} handleComplete={handleComplete} onToggleModal={onToggleModal} imgUrl={imgUrl} onClickFile={onClickFile} onChangeFile={onChangeFile} fileRef={fileRef} fileList={fileList} handlePreview={handlePreview} setFileList={setFileList} previewOpen={previewOpen} previewTitle={previewTitle} handleCancel={handleCancel} previewImage={previewImage} /> ); } import { Button, Image, Modal, Upload } from 'antd'; import DaumPostcodeEmbed from 'react-daum-postcode'; import { Main, Title } from '../../../commons/styles/emotion'; import * as S from './BoardWrite_styles'; import { type IBoardWrite_presenter_Props } from './BoardWrite_types'; import type { UploadProps } from 'antd/es/upload'; import { PlusOutlined } from '@ant-design/icons'; export default function BoardWrite_presenter( props: IBoardWrite_presenter_Props ) { const valid = props.valid; const isEditing = props.isEditing; const data = props.data?.fetchBoard; const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => { props.setFileList(newFileList); }; const uploadButton = ( <div> <PlusOutlined /> <div style={{ marginTop: 8 }}>Upload</div> </div> ); return ( <Main> <S.Form> <Title>게시물 {isEditing ? '수정' : '등록'}</Title> <div className="writer"> <S.InputWrapper> <label>작성자</label> <input id="writer" onChange={props.onChangeCoreInput} type="text" placeholder="이름을 입력해주세요." defaultValue={props.data?.fetchBoard.writer ?? ""} readOnly={!!props.data?.fetchBoard.writer} /> {props.writerError && ( <p className="alert">이름을 입력해주세요.</p> )} </S.InputWrapper> <S.InputWrapper> <label>비밀번호</label> <input id="password" onChange={props.onChangeCoreInput} autoComplete="off" type="password" placeholder="비밀번호를 입력해주세요." defaultValue={``} /> {props.passwordError && ( <p className="alert">비밀번호를 입력해주세요.</p> )} </S.InputWrapper> </div> <S.InputWrapper> <label>제목</label> <input id='title' onChange={props.onChangeCoreInput} type="text" placeholder="제목을 작성해주세요." defaultValue={data?.title} /> {props.titleError && ( <p className="alert">제목을 작성해주세요.</p> )} </S.InputWrapper> <S.InputWrapper> <label>내용</label> <textarea id='contents' onChange={props.onChangeCoreInput} placeholder="내용을 작성해주세요." defaultValue={props.data?.fetchBoard.contents} /> {props.contentsError && ( <p className="alert">내용을 작성해주세요.</p> )} </S.InputWrapper> <S.InputWrapper> <label>주소</label> <div className="zipcode"> <input id="zipcode" onChange={props.onChangeInput} type="text" // placeholder="00000" readOnly value={ props.address || (props.data?.fetchBoard.boardAddress?.address ?? "") } /> <button onClick={(e) => { e.preventDefault(); props.onToggleModal(); }} > 우편번호 검색 </button> {props.isOpen && ( <Modal title={'우편번호 검색'} open={props.isOpen} onOk={props.onToggleModal} onCancel={props.onToggleModal} > <DaumPostcodeEmbed onComplete={props.handleComplete} ></DaumPostcodeEmbed> </Modal> )} </div> <input id='address' onChange={props.onChangeInput} className="address" type="text" readOnly value={props.address !== "undefined" ? props.address : ""} /> <input id='addressDetail' onChange={props.onChangeInput} type="text" defaultValue={ data?.boardAddress?.addressDetail ? data?.boardAddress?.addressDetail : '' } /> </S.InputWrapper> <S.InputWrapper> <label>유튜브</label> <input id='youtubeUrl' onChange={props.onChangeInput} type="text" placeholder="링크를 복사해주세요." defaultValue={ data?.youtubeUrl ? data?.youtubeUrl : '' } /> </S.InputWrapper> <S.InputWrapper> <label>사진업로드</label> <Button onClick={props.onClickFile}>사진 업로드</Button> <input style={{ display: "none" }} ref={props.fileRef} onChange={props.onChangeFile} type="file" /> </S.InputWrapper> {props.imgUrl && <Image src={`https://storage.googleapis.com/${props.imgUrl}`}></Image>} <S.InputWrapper> <label>사진 첨부</label> <Upload // action="https://www.mocky.io/v2/5cc8019d300000980a055e76" listType="picture-card" fileList={props.fileList} onPreview={props.handlePreview} onChange={handleChange} > {props.fileList.length >= 8 ? null : uploadButton} </Upload> <Modal open={props.previewOpen} title={props.previewTitle} footer={null} onCancel={props.handleCancel}> <img alt={props.previewTitle} style={{ width: '100%' }} src={props.previewImage} /> </Modal> </S.InputWrapper> <S.InputWrapper> <div className="radios"> <span> <input type="radio" id="youtube" name="radios" value="youtube" defaultChecked onChange={props.onChangeImages} /> <label htmlFor="youtube">유튜브</label> </span> <span> <input type="radio" id="image" name="radios" value="image" onChange={props.onChangeImages} /> <label htmlFor="image">사진</label> </span> </div> </S.InputWrapper> <S.SubmitButton onClick={isEditing ? props.onUpdate : props.onSubmit} valid={valid} disabled={!valid} > {isEditing ? '수정하기' : '등록하기'} </S.SubmitButton> </S.Form> </Main> ); }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
Server Error Error: '@next/font' is only available in Next.js 13 and newer. 오류
이러한 오류떄문에 지금 만든 게시판등록 페이지가 실행이안됩니다 포트폴리오 01
-
해결됨만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
아직 못 들었는데 듣기 시작한 날부터 수강기한 시작할 수 없을까요?
개인 사정으로 결제만 해두고 강의를 하나도 듣지 못했는데,이제 시간이 조금씩 나서 듣기 시작하려고 합니다.그런데 강의 기한이 2달 조금 안되게 남아서 수강기한을 다시 설정할 수 없을까요?
-
미해결만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
사용하시는 zsh 테마가 뭐예요?
강의 화면에서 터미널을 보았습니다.사용하시는 쉘과 테마가 궁금합니다.감사합니다.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
회원가입 코드 작성후, 500에러
안녕하세요 강사님! 강의 잘 듣고 있습니다!!현재 회원가입 페이지 기능생성(3)을 듣고있는데요.회원가입 요청을 보내는 과정에서 500오류가 뜹니다. 일단 로그를 확인해봤는데요. 클라이언트는 문제가 없는것 같았고, server와 db에러로그를 살펴봤습니다.server에러로그server running at http://localhost:4000 Error: connect ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1159:16) { errno: -61, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 5432 } Error: DataSource is not set for this entity. at Function.getRepository (/Users/seokjiseon/Desktop/coding/clone/reddit/server/src/repository/BaseEntity.ts:115:19) at Function.findOneBy (/Users/seokjiseon/Desktop/coding/clone/reddit/server/src/repository/BaseEntity.ts:489:21) at /Users/seokjiseon/Desktop/coding/clone/reddit/server/src/routes/auth.ts:13:34 at Generator.next (<anonymous>) at /Users/seokjiseon/Desktop/coding/clone/reddit/server/src/routes/auth.ts:8:71 at new Promise (<anonymous>) at __awaiter (/Users/seokjiseon/Desktop/coding/clone/reddit/server/src/routes/auth.ts:4:12) at register (/Users/seokjiseon/Desktop/coding/clone/reddit/server/src/routes/auth.ts:6:56) at Layer.handle [as handle_request] (/Users/seokjiseon/Desktop/coding/clone/reddit/server/node_modules/express/lib/router/layer.js:95:5) at next (/Users/seokjiseon/Desktop/coding/clone/reddit/server/node_modules/express/lib/router/route.js:144:13) POST /api/auth/register 500 52.995 ms - 10 Error: DataSource is not set for this entity.이 문구를 보고, db와 연결이 안됐다고 추측하고 db로그를 확인했습니다. db로그Attaching to postgresdocker-compose up을 입력하면, 여기서 넘어가지 않더라구요.ㅠㅠ 구글링해보니 docker-compose up -d 를 입력하면 실행로그를 자세히 볼수 있다고 해서 입력해 봤는데요. 아래 오른쪽 터미널 창에 뜨는게 무한히 반복되는 상태만 지속되고 있었습니다.. 백엔드 지식이 전무해서, docker랑 db를 전부 처음 다뤄봐서 혼자 해결하기 어려워서 질문 남깁니다ㅠㅠ혹시 어떤부분을 더 살펴봐야 할까요??ㅠㅠ
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
UI 재사용성 질문
export const Row = styled.div` display: flex; flex-direction: row; width: 100%; justify-content: space-between; padding: 0 24px; margin: 8px 0; `; <Row style={{ justifyContent: "start" }}> <InputFieldSmall style={{ marginRight: "16px" }}> <Input placeholder="07250" /> </InputFieldSmall> <ButtonBlack>우편번호 검색</ButtonBlack> </Row>안녕하세요!컴포넌트 재사용성에 관하여 질문드리고 싶은게 있습니다!1일차 게시글 작성 UI 만들기 과제를 진행하던 중 CSS 스타일을 재사용 하기 위해서 Row라는 컴포넌트를 만들었습니다.UI상 justify-content속성에 space-between 또는 flex-start 같이 약간만 변하는 부분이 존재해서 inline-stlye을 사용하였습니다.제공 해주신 레퍼런스 코드에는 inline-style을 사용하지 않으셨는데 inline-style을 사용하는 것은 좋지 않은 방식인가요?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
Nextjs gh-pages 배포
안녕하세요~Nextjs 강의 부분까지 수업을 마쳤습니다.선생님께서 nextjs & typescript로 제작하신 블로그 부분을 gh-pages를 사용해서 배포해보려는데 디렉토링 부분이나 렌더되는 부분이 기존의 react 부분과 달라 질문 드립니다. baseurl 설정은 어디서 해야할까요?deploy 방식은 동일한 건가요?혹시 위의 질문 말고도 다른점이 있을까요?
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
SSG 설명에서 나온 Disable cache 의 다른 렌더링방식들과 비교
안녕하세요!SSG 관련 설명해주실 때 개발자도구에서 Disable cache를 하면서static 데이터가 이미 들어와있고 etag가 여전히 그대로인 것으로 캐시와 상관 없이 해당 static 데이터가 영향을 주는 것을 확인해주셨는데요. 그렇다면 SSR, CSR에서 Disable cache 했을시 위의 현상과 다른게 있나요 ... ?SSR은 서버에서 계속 보낼테니 cache랑 상관 없을 것 같고 ... CSR도 JS 파일을 들고 있을테니 이 역시 상관 없을 것 같아서요...!혹시 CSR 특징에서 언급해주신 'JS 캐시 가능' 과 관련이 있다면 브라우저가 JS 캐시를 어떻게 활용하는지도 궁금합니다!감사합니다!
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
배포 후 로그인 인증이 안됩니다..
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 현재 강의대로 배포중인 상태입니다.회원가입도 잘 되어서 로그인도 되는데로그아웃이란 커뮤니티 생성 시 401 (Unauthorized)에러가 뜹니다.. 로컬에서는 아무런 문제 없이 잘 작동하고 있습니다.아무리 찾아봐도 어떤 문제가 있는지를 모르겠습니다 ㅠㅠ