묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
데이터 페칭이 되면 React Query를 대체할 수 있을까요?
강의를 들어보니 React Query와 꽤 비슷한 부분이 많다고 생각되서 혹시 대체할 수 있을까 생각이 들었는데요. 정환님은 어떤 생각을 가지고 계신지 궁금하여 질문 드립니다. 만약 App Router로 새로운 프로젝트를 구축해야 한다고 하면 React Query를 사용하실건가요?
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
db관련 질문있습니다.
postgres 주소는 오프라인 강의 시에만 제공하고 온라인은 제공을 하지 않는다고 이해했는데요.해당 부분으로 대체 가능한 방법과 postgres 없이 graphql만 실행하여 강의를 따라가도 지장이 없는지 궁금합니다.이후 강의에는 데이터 처리하는 부분이 포함되어있는 것 같은데요. 해당부분 확인 바랍니다!
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
AxiosError {message: 'Request failed with status code 401/500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE',
로그인 페이지에서 로그인을 하면 500, 커뮤니티 생성 페이지에서 생성을 하면 401 AxiosError가 뜹니다. <login.tsx>import axios from 'axios'; import InputGroup from '../components/InputGroup' import Link from 'next/link' import { useRouter } from 'next/router'; import React,{ FormEvent, useState } from 'react' import { useAuthDispatch } from '../context/auth'; axios.defaults.withCredentials = true; const Login = () => { let router = useRouter(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [errors, setErrors] = useState<any>({}); const dispatch = useAuthDispatch(); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); try{ const res = await axios.post("/auth/login", {password, username}, {headers: {'Access-Control-Allow-Origin': 'http://localhost:3000'}}) dispatch("LOGIN", res.data?.user); } catch(error: any){ //console.log(error); setErrors(error.response?.data || {}) } } return ( <div className='bg-white'> <div className='flex flex-col items-center justify-center h-screen p-6'> <div className='w-10/12 mx-auto md:w-96'> <h1 className='mb-2 text-lg font-medium'>로그인</h1> <form onSubmit={handleSubmit}> <InputGroup placeholder='Username' value={username} setValue={setUsername} error={errors.username} /> <InputGroup placeholder='Password' value={password} setValue={setPassword} error={errors.password} /> <button className='w-full py-2 mb-1 text-xs font-bold text-white uppercase bg-gray-400 border border-gray-400 rounded'> 로그인 </button> </form> <small> 아직 아이디가 없으신가요? <Link href="/register" legacyBehavior> <a className='ml-1 text-blue-500 uppercase'>회원가입</a> </Link> </small> </div> </div> </div> ) } export default Login<create.tsx>import axios from "axios"; import { GetServerSideProps } from "next"; import InputGroup from "../../components/InputGroup" import {useState, FormEvent} from "react"; import {useRouter} from "next/router" axios.defaults.withCredentials = true; const SubCreate = () => { const [name, setName] = useState(""); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [errors, setErrors] = useState<any>({}); let router = useRouter(); const handleSubmit = async (event: FormEvent) => { event.preventDefault(); try { const res = await axios.post("/subs", {name, title, description}, {headers: {'Access-Control-Allow-Origin': 'http://localhost:3000'}}) router.push(`/r/${res.data.name}`); } catch (error: any) { // console.log(error); setErrors(error.response?.data || {}); } } return ( <div className="flex flex-col justify-center pt-16"> <div className="w-10/12 mx-auto md:w-96"> <h1 className="mb-2 text-lg font-medium"> 그룹 만들기 </h1> <hr /> <form onSubmit={handleSubmit}> <div className="my-6"> <p className="font-medium">Name</p> <p className="mb-2 text-xs text-gray-400"> 그룹 이름은 변경할 수 없습니다. </p> <InputGroup placeholder="이름" value={name} setValue={setName} error={errors.name} /> </div> <div className="my-6"> <p className="font-medium">Title</p> <p className="mb-2 text-xs text-gray-400"> 주제를 입력해 주세요. </p> <InputGroup placeholder="주제" value={title} setValue={setTitle} error={errors.title} /> </div> <div className="my-6"> <p className="font-medium">Description</p> <p className="mb-2 text-xs text-gray-400"> 그룹에 대한 설명을 입력해주세요. </p> <InputGroup placeholder="설명" value={description} setValue={setDescription} error={errors.description} /> </div> <div className="flex jstify-end"> <button className="px-4 py-1 text-sm font-semibold rounded text-white bg-gray-400 border" > 그룹 만들기 </button> </div> </form> </div> </div> ) } export default SubCreate<subs.ts>import {Router, Request, Response} from "express"; import jwt from "jsonwebtoken" import { User } from "../entities/User"; import userMiddleware from "../middlewares/user" import authMiddleware from "../middlewares/auth" import { AppDataSource } from "../data-source"; import Sub from "../entities/Sub"; import { isEmpty } from "class-validator"; const createSub = async (req: Request, res: Response, next) => { const {name, title, description} = req.body; try { let errors: any = {}; if (isEmpty(name)) errors.name = "이름은 비워둘 수 없습니다."; if (isEmpty(title)) errors.title = "제목은 비워둘 수 없습니다."; const sub = await AppDataSource.getRepository(Sub) .createQueryBuilder("sub") .where("lower(sub.name) = :name", { name: name.toLowerCase() }) .getOne(); if (sub) errors.name = "서브가 이미 존재합니다."; if (Object.keys(errors).length > 0) { throw errors; } } catch (error) { console.log(error); return res.status(500).json({ error: "문제가 발생했습니다." }); } try { const user: User = res.locals.user; const sub = new Sub(); sub.name = name; sub.description = description; sub.title = title; sub.user = user; await sub.save(); return res.json(sub); } catch (error) { console.log(error); return res.status(500).json({ error: "문제가 발생했습니다." }); } }; const router = Router(); router.post("/", userMiddleware,authMiddleware, createSub); export default router; <server.ts>import express, { response } from "express"; import morgan from "morgan"; import { AppDataSource } from "./data-source"; import authRoutes from './routes/auth' import subRoutes from './routes/subs' import cors from 'cors'; import dotenv from 'dotenv'; import cookieParser from "cookie-parser"; const app = express(); const origin = process.env.ORIGIN; const corsOption = { origin: "http://localhost:3000", credentials: true } app.use(cors(corsOption)) app.use(express.json()); app.use(morgan('dev')); app.use(cookieParser()); dotenv.config(); app.get("/", (_, res) => {res.send("running")}); app.use("/api/auth", authRoutes) app.use("/api/subs", subRoutes) console.log(process.env.ORIGIN) let port = 4000; app.listen(port, async () => { console.log('server running at http://localhost:${port}'); AppDataSource.initialize().then(async () =>{ console.log("database initialized") }).catch(error => console.log(error)) }) 아무리 구글링해봐도 기본 지식이 부족해서 도저히 해결이 안되네요ㅠㅠ 조금 급하게 만들어야 하는 상황이라.... 도와주세요...!
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
Image의 src에 경로를 작성하는 것과 import로 이미지를 가져와서 넣는 것의 차이가 궁금합니다!
<Image src="/logo.png" alt="로고" width={100} height={100} /> import Logo from "@/public/logo.png"; <Image src={Logo} alt="로고" width={100} height={100} />이 두가지 접근 방식의 차이가 있을까요?
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
포괄적 에러 핸들링 error.tsx이 정상 작동 하지 않습니다 (05:30)
안녕하세요서버는 종료한 상태에서 강의대로 (with-searchbar)/error.tsx 파일을 만들고 새로고침을 하면 error.tsx에서 작성한 페이지가 나오지 않습니다.error.tsx 인덱스 페이지 아래 사진처럼 error.tsx페이지가 아닌핸들링되지 않은 런타임 에러가 발생해버립니다..혹시 searchParams를 Promise객체로 타입정의 한것처럼 뭔가 사용방법이 바뀐걸까요..?
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
Next.js SSR 방식에 대해 질문 드립니다.
안녕하세요 강의 잘 듣고 있습니다. SSR 의 동작방식에 대해 문의 드립니다. 1) 프로젝트에 초기 접속할때는 '/' 페이지에 대한 HTML 반환 후 js bundle 반환 => 하이드레이션 2) '/search' 로 이동하면 '/search' 페이지에 대한 HTML 반환 후 캐싱된 js bundle 과 결합하여 하이드레이션저는 대충 위 과정으로 이해하고 있었는데, 페이지에 따라 js bundle을 추가로 요청하는 경우도 있다고 해서 헷갈리는 것 같습니다. 초기 접속과 이후 새로운 페이지를 요청할때 HTML 응답과 js bundle 응답이 어떤식으로 동작하는지 설명 부탁 드립니다!
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
skeleton UI 적용 시점
안녕하세요 현재 6.4)스켈레톤 UI 적용하기 강의를 듣고나서 궁금한점이 있어 질문 남깁니다. 서버쪽 데이터요청이 오래 걸리는 경우 Suspense를 통해 Skeleton UI를 보여주고 이후 데이터가 적용 된 컴포넌트를 보여주는거로 알고 있습니다.그래서 사용자경험이 조금 오를 것 같긴한데, 만약 데이터 요청이 빠른 경우(0.5초만에 데이터처리가 이뤄진 경우)에는 오히려 UX 관점에서 불편하지 않을까 싶어서 이럴땐 어떻게 처리하는게 좋을 지 질문 남깁니다!예시) 책 검색 -> 책 리스트의 skeleton UI 가 잠깐 보였다가(0.5초 등장) -> 책 리스트 나타남 (이와 같은 과정이 이뤄지면 오히려 사용자 경험이 떨어지지 않을까 싶어 궁금합니다)
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
2-16 ssg 구현중 에러입니다.
안녕하세요 강사님, 강의를 듣는 중 문제가 발생해 글 남깁니다.book/id 상세페이지에서 SSG getStaticPaths를 적용중인데, id를 인식을 못해 에러가 발생합니다.Error: A required parameter (id) was not provided as an array received string in getStaticPaths for /book/[[...id]]강의에 의하면, books/1 은 보여야 하는데 페이지가 보이지 않네요오타인 것 같아 강사님 깃헙 코드도 복사해서 붙여봤는데 에러가 납니다 혹시 다른 문제가 있을까요? 에러 메세지에는 id가 제공이 안된다고 하는데, 제가 보기에는 staticProps에 맞게 변경한거 같은데, 왜 에러가 날까요??
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
overload 에러
import {Router, Request, Response} from "express"; import {User} from "../entities/User"; import { validate, Validate } from "class-validator"; const register = async (req: Request, res: Response) => { const {email, username, password} = req.body; try{ let errors: any = {}; //이메일/유저이름 단일성 확인 const emailUser = await User.findOneBy({email}); const usernameUser = await User.findOneBy({username}); //이미 있으면 erros 객체에 넣음 if(emailUser) errors.email = "이미 해당 이메일 주소가 사용되었습니다." if(usernameUser) errors.username = "이미 사용자 이름이 사용되었습니다." //에러가 있으면 return으로 에러를 response 보내줌 if(Object.keys(errors).length > 0){ return res.status(400).json(errors) } const user = new User(); user.email = email; user.username = username; user.password = password; //엔터티에 정해 놓은 조건으로 user 데이터 유호성 검사를 해줌 errors = await validate(user); //유저 정보를 user table에 저장 await user.save() return res.json(user); } catch(error){ console.error(error); return res.status(500).json({error}) } } const router = Router(); router.post("/register", register); export default router맨 위 코드(auth.ts)에서 사진과 같이 overload 에러가 뜹니다.유저이름/이메일 중복 및 에러 처리하는 코드 중 return으로 응답을 반환하는 중 타입이 맞지 않아서 생기는 오류 같은데 어떻게 해결할 수 있을까요??
-
해결됨[풀스택 완성] Supabase로 웹사이트 3개 클론하기 (Next.js 14)
채팅기능 getAllUsers 질문드립니다.
Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding 'use client' to a module that was originally written for the server.이라는 에러가 뜨고 유저리스트가 빈배열 리턴되는데 이런 경우 어떻게 처리해야하나요?
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
검색 결과 페이지네이션 1페이지(일단해결)
결론부터 말하면 검색할 때 페이지네이션 현재 페이지가 1로 안 바뀌어서 바꿔봤습니다.예를 들어 ‘철’이라는 검색어를 입력했더니 페이지가 2페이지까지 있음. 2페이지를 클릭해 2페이지가 보이는 상태에서 검색어를 ‘철수’로 바꾸니 게시글 수가 1페이지까지밖에 없었는데, 1페이지가 현재 activedPage가 아니게 됨(없어진 2페이지 그대로).검색한 게시글 목록 refetch는 제대로 이루어지기에 1페이지의 내용이 보이는 것은 맞지만 현재 페이지 숫자가 제대로 표시되지 않음. 또한 ‘안녕하세요’의 12페이지를 보고 있었는데 ‘안녕’으로 검색어를 바꾸니 당연히 refetch를 통해 1페이지 게시글 목록을 보여주지만, 페이지네이션은 그대로 11, 12, ..., 20임검색어가 바뀌면 페이지네이션의 activedPage와 startPage를 1로 변경해주도록 Paginations01.container에서 useEffect 실행시켜 해결 useEffect(() => { setStartPage(1); setActivedPage(1); }, [props.count]);검색어를 바꾸면 검색된 총 게시글 수도 보통 달라질 거라는 생각에 props.count를 의존성으로 써본건데 검색어를 바꿔도 우연히 검색된 총 게시글 수가 똑같을 경우 useEffect 실행 안 되니정말로 검색어가 달라질 때를 기준으로 하려고BoardList에 있는 keyword를 pagination 컴포넌트에 props로 넘겨줘서 사용// BoardList.presenter.tsx <Paginations01 refetch={props.refetch} count={props.count} keyword={props.keyword} /> // keyword 전달 // Paginations01.container.tsx useEffect(() => { setStartPage(1); setActivedPage(1); }, [props.keyword]); // Paginations01.types.ts export interface IPaginations01Props { ... keyword?: string; ... } 고쳐지긴 했는데 일단 useEffect에서 setState를 하고 있다는 점(여기선 크게 상관이 없는건지?)과페이지네이션 컴포넌트인데 keyword라는 걸 받아온다는 것 자체가 검색 컴포넌트의 영향을 받는다는 뜻 아닌가 싶어서(keyword는 검색 기능이 있을 때만 존재하니까) 제대로 된 컴포넌트 분리에 대한 고민(keyword?로 하면 페이지네이션 컴포넌트 쓸 때 keyword 써도 되고 안 써도 되니까 이대로 해도 괜찮을지...?)다른 방법은 있는지?의견 있으시면 알려주시면 감사하겠습니다. 그외 궁금한 점저는 이거 하는 과정에서 yarn dev 해놓은 상태에서 코드들을 저장 계속 하면서 고쳤는데 그럼 원래 바로바로 반영이 되잖아요. 코드에는 문제가 없어서 제대로 작동해야 했는데 페이지네이션 컴포넌트에서 props.keyword가 자꾸 undefined인 바람에 useEffect도 작동 안해서코드가 문젠줄 알고 한참 삽질 후 그냥 vscode와 크롬 등등 다 끄고 좀 나중에 그대로 다시 켜보니까 잘 작동하게 됐는데 (yarn dev를 끊었다 다시 하는 거로는 아마 해결 안됐음)이게 처음이 아니고 저번에도 한 번 포기하고 결국 다음날 켜보니까 코드는 그대로인데 갑자기 잘 되었는데정확히 무엇때문에 이런 문제가 생기는건지 모르겠네요!!! (캐시? .next? 뭐 그런 거 때문인가요?)
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
클라이언트 컴포넌트에서 서버 컴포넌트 임포트
수업에서 Next.js에서는 클라이언트에 import 되는 서버 컴포넌트를 자동으로 클라이언트 컴포넌트로 변경해주는 동작을 한다. 라고 하셨는데요. 공식문서나 구글 문서를 아무리 찾아봐도 관련 언급이 없던데요. 혹시 출처를 알수 있을까요? 언제 업데이트 된건지 확인해보고 싶어서요
-
미해결한 입 크기로 잘라먹는 Next.js(15+)
6.5 에러핸들링 24:04 (with-searchbar) 레이아웃이 살아나지 않음
6.5 에러핸들링 24:04 (with-searchbar) 레이아웃을 살리기위해 error.tsx를 app/book/[id] 로 옮겨도 <Searchbar />가 보이지 않는 현상이 있습니다. 그리고 영상에서도 app/book/[id] 가 에러인 상황임에도 검색바 레이아웃이 보이는 장면이 없어서 헷갈리는 것 같습니다!
-
미해결한 입 크기로 잘라먹는 Next.js(15+)
app-router route handler 에러 처리방법
import { type NextRequest, NextResponse } from 'next/server'; import { fetchData } from '@/shared/apis/fetch-data'; import type { IAcademyCreateDTO, IPostEnrollAcademyResponse } from '@/shared/types/acadmy'; export async function POST(req: NextRequest) { const body = (await req.json()) as Promise<IAcademyCreateDTO>; const data = await fetchData<IPostEnrollAcademyResponse>('/api/v1/academies', 'POST', body); if (!data.isSuccess) { return NextResponse.json( {}, { status: 500, statusText: data.message, }, ); } return NextResponse.json(data); }서버에, 에러시 아래와 같은 데이터 구조를 준다고 했을떄 위의 방식처럼, 에러 핸들링을 하는 것이 맞는지 궁금합니다... Route Handler의 보편적인 에러처리 방식이 너무 궁금합니다!! (직접 에러 메시지를 작성하는것이 아닌 서버에서 내려주는 에러메시지를 화면에 표기하고 싶습니다.)export type CommonResponse<T = unknown> = { code: string; isSuccess: boolean; message: string; result: T; };
-
미해결Next + React Query로 SNS 서비스 만들기
next-auth의 refresh token rotation에 관한 질문입니다.
안녕하세요 next-auth로 토큰 갱신 로직을 개발하는 중에 해결되지 않는 문제가 발생하여 질문 드립니다.현재 클라이언트에서 api 요청을 하면 middleware에서 const token = await auth(); 이런식으로 세션 정보를 가져와 accessToken을 헤더에 삽입하는 방식을 구현하였습니다. if (request.nextUrl.pathname.startsWith("/gateway")) { const session = await auth(); const accessToken = session?.accessToken; const { device } = userAgent(request); const localeFromCookie = cookieStore.get("dp_lang")?.value as string; const defaultLocale = getLanguageCodeFromLocale(localeFromCookie); request.headers.set("Accept", "*/*"); request.headers.set("Authorization", `Bearer ${accessToken}`); request.headers.set("Access-Control-Allow-Origin", "*"); request.headers.set("deviceType", "1"); request.headers.set("deviceId", getDeviceIdFromCookie(cookieStore)); request.headers.set("osType", getOsTypeFromCookie(cookieStore)); request.headers.set("User-Agent", device.model ?? ""); request.headers.set("locale", localeFromCookie); request.headers.set("language", defaultLocale); const response = NextResponse.next({ request: { headers: request.headers, }, }); return response; } 따라서 미들웨어에서 세션에 접근하는 순간에 jwt콜백이 실행되어 jwt 토큰에 저장되어 있는 토큰 expire 시간을 비교하여 만료여부를 판단 한 뒤에 토큰 갱신이 되도록 구현을 하였습니다. async jwt({ token, account, user }) { // Initial sign in if (account && user) { return { accessToken: user.accessToken, expiresAt: new Date( Date.now() + (user?.expiresIn ?? 0) ).toISOString(), refreshToken: user.refreshToken, serviceAvailable: true, }; } // Return previous token if the access token has not expired yet if ( new Date() < new Date(new Date(token.expiresAt as string).getTime() - 5000) ) { console.log("@@@@@@valid"); return token; } else { if (token.error === "RefreshTokenInvalid") { return token; } // Access token has expired, try tㄴo update it try { console.log("@@@@@@expired"); const refreshedTokens = await authApi.refreshAuthToken({ refreshToken: token.refreshToken as string, accessToken: token.accessToken as string, }); console.log("==========token refreshed========"); // reissue token return { ...token, accessToken: refreshedTokens.accessToken, expiresAt: new Date( Date.now() + (refreshedTokens.expiresIn ?? 18000) ).toISOString(), refreshToken: refreshedTokens.refreshToken ?? token.refreshToken, error: null, }; } catch (error) { console.error("Token refresh failed", error); if (error.status === RestApiErrorType.invalidTokenException) { return { ...token, error: "RefreshTokenInvalid", }; } return { ...token, error: "RefreshAccessTokenError" }; } } }, 기존에는 세션에 접근을 할때 next-auth의 jwt 콜백이 돌고 값이 리턴되면 쿠키의 session-token값에 암호화된 값들이 업데이트 되는 걸로 알고 있었는데 찾아보니 현재 세션에 접근하고 갱신하는 부분이 서버단에 있어서 쿠키가 업데이트 되지 않는다고 합니다. 따라서 현재 토큰이 만료된 후 한번에 3가지의 api 콜을 요청했을때 첫번째 api요청에서는 정상적으로 토큰 refresh가 요청이 되며 middleware에서 세션에 접근해 갱신된 토큰이 가져와져 요청이 성공되나 그 뒤 요청부터는 쿠키에 업데이트가 되지 않아서인지 세션에서 갱신 전 토큰이 가져와지는 현상이 발생하고 있습니다.클라이언트에서 세션 업데이트 후 세션에 접근을 해야 쿠키값이 갱신이 된다고 하는데 다른 방법이 있는지 문의드립니다.
-
해결됨한 입 크기로 잘라먹는 Next.js(15+)
컴포넌트 스트리밍 적용하기에서 searchParams 비동기 변경관련 질문
안녕하세요.컴포넌트 스트리밍 적용하기에서 searchParams가 Promise 값을 반환하는 형태로 바뀌었는데요.Suspense에서 key 값을 어떻게 처리하나요?searchParams 자체는 비동기 처리하는 함수를 분리해서 검색이 가능하게는 처리가능한데 Suspense는 마땅한 방법이 없어보여서 질문 드려봅니다.searchParams: Promise<{ q?: string; }>;
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
for in for of 강의에서 starter() 함수가 starter is not defined
const messageContainer = document.querySelector("#d-day-message"); const container = document.querySelector("#d-day-container"); container.style.display = 'none' messageContainer.innerHTML = "<h3>D-Day를 입력해 주세요</h3>"; const dateForMaker = function () { const inputYear = document.querySelector("#target-year-input").value; const inputMonth = document.querySelector("#target-month-input").value; const inputDate = document.querySelector("#target-date-input").value; //const dateFormat = inputYear + "-" + inputMonth + "-" + inputDate; const dateFormat = `${inputYear}-${inputMonth}-${inputDate}`; return dateFormat; // console.log(inputYear, inputDate, inputMonth); }; const counterMaker = function () { const targetDateInput = dateForMaker(); // console.log(targetDateInput); const nowDate = new Date(); const targetDate = new Date(targetDateInput).setHours(0, 0, 0, 0); const remaining = (targetDate - nowDate) / 1000; // 만약 remaining이 0이라면 , 타이머가 종료 되었습니다 출력 (수도코드) console.log(remaining); if (remaining === 0 || remaining < 0) { // console.log("타이머가 종료되었습니다"); messageContainer.innerHTML = "<h3>타이머가 종료되었습니다</h3>"; } else if (isNaN(remaining)) { // 만약 잘못된 날짜가 들어왔다면, 유효한 시간대가 아닙니다 출력 // console.log("유효한 시간대가 아닙니다"); messageContainer.innerHTML = "<h3>유효한 시간대가 아닙니다</h3>"; } // const remainingDate = Math.floor(remaining / 3600 / 24); //Math. floor 소숫점 제거 // const remaingHours = Math.floor(remaining / 3600) % 24; // const remaingMin = Math.floor(remaining / 60) % 60; // const remaingSec = Math.floor(remaining) % 60; const remaingObj = { remainingDate: Math.floor(remaining / 3600 / 24), remaingHours: Math.floor(remaining / 3600) % 24, remaingMin: Math.floor(remaining / 60) % 60, remaingSec: Math.floor(remaining) % 60, }; // const days = document.getElementById("days"); // const hours = document.getElementById("hours"); // const min = document.getElementById("min"); // const sec = document.getElementById("sec"); // const documentObj = { // days: document.getElementById("days"), // hours: document.getElementById("hours"), // min: document.getElementById("min"), // sec: document.getElementById("sec"), // }; const documentArr = ['days', 'hours', 'min' , 'sec'] // const docKeys = Object.keys(documentObj); const timeKeys = Object.keys(remaingObj); // Object.keys : 객체의 키를 가져와 배열로 반환f let i = 0; for (let tag of documentArr) { // 배열로 이용한다 document.getElementById(tag).textContent = remaingObj[timeKeys[i]] i++ } const starter = function () { container.style.display ='flex' messageContainer.style.display = 'none' counterMaker() } // for (let i = 0; i < timeKeys.length; i = i + 1) { for문 // documentObj[docKeys[i]].textContent = remaingObj[timeKeys[i]]; // // console.log(timeKeys); // // console.log(timeKeys[i]); // } // let i = 0; // for (let key in documentObj) { // 객체로 이용한다 for in // documentObj[key].textContent = remaingObj[timeKeys [i]] // i++; // } // documentObj['days'].textContent = remaingObj["remainingDate"]; // documentObj['hours'].textContent = remaingObj["remaingHours"]; // documentObj['min'].textContent = remaingObj["remaingMin"]; // documentObj['sec'].textContent = remaingObj["remaingSec"]; // console.log("클릭"); // console.log(remainingDate, remaingHours, remaingMin, remaingSec); };<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="./main.css" /> <!-- <script src="./script.js" defer></script> --> <title>Document</title> </head> <body> <h1>D-Day</h1> <div id="d-day-container"> <div class="d-day-child-container"> <span id="days">0</span> <span>일</span> </div> <div class="d-day-child-container"> <span id="hours">0</span> <span>시간</span> </div> <div class="d-day-child-container"> <span id="min">0</span> <span>분</span> </div> <div class="d-day-child-container"> <span id="sec">0</span> <span>초</span> </div> </div> <div id="d-day-message"></div> <div id="target-selector"> <input type="text" id="target-year-input" class="target-input" / size="5"> <input type="text" id="target-month-input" class="target-input" / size="5"> <input type="text" id="target-date-input" class="target-input" / size="5"> </div> <button onclick="starter()" id="start-btn">카운트 다운 시작</button> <script src="./script.js"></script> </body> </html>아무리 호출하고 수정해도 계속 오류가 납니다 이유 좀 알려주세요 ㅠ
-
미해결Next + React Query로 SNS 서비스 만들기
nextjs middleware에서 쿠키 업데이트에 관한 질문입니다.
안녕하세요 프로젝트 진행 중에 해결되지 않는 부분이 있어 질문 드립니다.middleware에서 쿠키의 값을 업데이트 하려고 하는데 업데이트 되지 않는 현상이 발생하고 있습니다.아래의 코드와 같이 NextResponse.next() 실행 후에 response에 쿠키를 업데이트를 하려 하는데 반영이 되지 않습니다. request에서 세팅해도 마찬가지입니다. 쿠키 세팅이 되지 않는 원인에 대해 알고 계신가요?? export default async function middleware(request: NextAuthRequest) { if (request.nextUrl.pathname.startsWith("/gateway")) { const token = await getToken({ req: request, secret: process.env.AUTH_SECRET as string, secureCookie: process.env.NODE_ENV === "production", }); const accessToken = token?.accessToken; const { device } = userAgent(request); request.headers.set("Accept", "*/*"); request.headers.set("Authorization", `Bearer ${accessToken}`); request.headers.set("Access-Control-Allow-Origin", "*"); request.headers.set("deviceType", "1"); request.headers.set("User-Agent", device.model ?? ""); request.headers.set("locale", localeFromCookie); request.headers.set("language", defaultLocale); const response = NextResponse.next({ request: request.headers }); response.cookies.set("test", "test"); return response; } ... }
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
섹션8. ▶ openWeatherMap API 부분 질문있습니다.
const weatherSearch = function (position) { fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${position.latitude}&lon=${position.longitude}&exclude={part}&appid=b53e0e301571ed81576201a2a4fee23b` ); }; const accessToGeo = function (position) { const positionObj = { latitude: position.coords.latitude, longitude: position.coords.longitude, }; weatherSearch(positionObj); console.log(position.latitude); }; const askForLocation = function () { navigator.geolocation.getCurrentPosition(accessToGeo, (err) => { console.log(err); }); }; askForLocation();위 코드 13번째 줄에서console.log(position.latitude);를 하면저는 undefined가 출력되고,console.log(position.coords.latitude);를 해야위도 값이 나오는데...강사님께서는 console.log(position.latitude); 라고 코드를 작성하셨는데,위도 값이 잘 나오더라고요.. 이게 왜 그런지 별건 아니지만 궁금해서 질문남깁니다!
-
미해결Next.js 시작하기
<img /> 요소 대신 <Image /> 컴포넌트를 사용해야 하는 이유 (성능 최적화)
섹션 11의 이미지 성능 최적화 강의에서 Next.js의 이미지 성능 최적화에 대해 설명하실 때의 강의 코드가 <Image /> 컴포넌트가 아닌 <img /> 요소인데도 이미지 성능 최적화가 잘 되는 것을 보았습니다. 그럼 굳이 <img /> 요소 대신 <Imgae /> 컴포넌트를 사용해야 하는 이유가 있나요?