inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

173만명의 커뮤니티!! 함께 토론해봐요.

투두 추가할 때 onChangeContent 작성 이유

해결됨

한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지

8.4) Create - 투두 추가하기 강의를 들으며 4분대 경 코드를 작성 중입니다. import "./Editor.css"; import { useState } from "react"; const Editor = ({ onCreate }) => { const [content, setContent] = useState(""); const onChangeContent = (e) => { setContent(e.target.value); }; const onSubmit = () => { onCreate(); }; return ( <div className="Editor"> <input value={content} onChange={onChangeContent} placeholder="새로운 Todo..." /> <button onClick={onSubmit}>추가</button> </div> ); }; export default Editor; 이 부분에서, input에 들어가는 content는 추가 버튼을 클릭할 때만 value를 setContent로 해줘도 될 것 같은데요, 왜 onChangeContent로 값이 바뀔 때마다 밸류를 저장해주는지 궁금합니다. 타이핑할때마다(값이 바뀔 때마다) 저장할 필요없이 마지막에 추가할 때만 저장되어도 되지 않나 싶어서 궁금해졌습니다.

  • javascript
  • react
  • node.js
ryucream612 댓글 1 좋아요 0 조회수 370

강사님 이렇게 풀어봤습니다.!

미해결

자바스크립트 알고리즘 문제풀이 입문(코딩테스트 대비)

const solve = (arr) => { const result = [] for (let i = 0; i < arr.length; i++) { let count = 1 for(let j = 0; j < arr.length; j++) { if (arr[j] > arr[i]) { count += 1; } } result.push(count); } return result } 조금 간단...해..보이게..? 처음에 count 를 1로 줘서 1등으로 시작하고 풀이해주신 부분과 비슷하지만 살짝은,, 다르게 풀어봤습니다.

  • javascript
  • 코딩-테스트
hollis9797 댓글 1 좋아요 0 조회수 183

24/06/10 기준으로 게시글 생성 API 403 Forbidden Err

미해결

Next + React Query로 SNS 서비스 만들기

지난 이틀전 토요일에서는 문제없이 작동했었는데, 갑자기 게시글 작성 api가 제대로 요청을 받지 않는 것 같습니다. 스웨거로도 요청 보내보았는데, 되지 않아 질문 올렸습니다!

  • react
  • next.js
  • react-query
  • next-auth
  • msw
hwuiinn 댓글 1 좋아요 0 조회수 230

리액트와 next.js 강의에 대해 여쭤볼게 있습니다.

해결됨

[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스

안녕하세요 우선 강의 열심히 잘 보고 있습니다. 다른게 아니라 리액트와 next.js 수업이 조금 헷갈리는데 섹션5부분만 리액트이고 그 뒤에는 next.js라고 보면 되나요? 강의를 보다가 어느순간 제가 알고있는 리액트의 폴더구조들이 조금 다르더라구요. 리액트에서는 폴더중에 App.js인데 _app.js 이런식으로 되어있고 또 마지막 포트폴리오 리뷰에서는 화면에 리액트 로고부분도 나와있어서 어디서부터 어디까지가 리액트이고 next.js 강의인지 알려주시면 너무 감사드리겠습니다 ^^ 마지막으로 포트폴리오 리뷰는 제가 어떠한걸 보고 만든 후 보는 영상인지 아니면 그냥 영상을 보고 같이 하는건지도 알고싶습니다.

  • react
  • node.js
  • seo
  • graphql
  • next.js
부드러운 족제비 댓글 1 좋아요 0 조회수 219

액세스 토큰 재발급 관련하여 질문이 있습니다.

미해결

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

안녕하세요 강의 잘 듣고 있습니다. 액세스 토큰이 만료되었다고 가정했을 때 프론트에서 특정 API(예시. post 조회)를 호출하면 토큰이 만료되었다는 에러 메시지를 받죠. 그러면 앱에서 토큰만료 리스폰스가 도착하면 토큰을 재발급 받은 후 다시 post를 조회하는 API를 호출하는건가요?? 고객의 입장에서는 위 내용을 알 필요가 없을 것 같아서요. (c. 클라이언트, s.서버) C.API 호출 -> [ S.만료되었다는 익셉션 -> C.재발급 요청 -> S.토큰 재발급 -> C.다시 API호출 ] -> S.응답 반환 위 순서도에서 대괄호 친 부분은 클라이언트에서 알아서 다시 처리하는건지 궁금합니다.

  • javascript
  • typescript
  • rest-api
  • nestjs
  • backend
Jin Su Yu 댓글 1 좋아요 0 조회수 555

섹션26 FileIntercepter 업로드 파일 질문

미해결

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

다른것은 다 문제가 없다만, 업로드 public/post파일에 이미지가 안들어옵니다일부러 폴더이름과 변수명은 다르게 했습니다 @Module({ imports: [ TypeOrmModule.forFeature([Event]), MulterModule.register({ fileFilter: (_req, file, cb) => { extname(file.originalname) return cb(null, true) }, storage: multer.diskStorage({ destination: (_req, _res, cb) => { cb(null, EVENT_IMG_PATH) }, // 파일 이름이 겹치지않게 고유한 uuid값을 이미지 고유 이름 앞에 붙여줌 filename: (_req, file, cb) => { cb(null, `${uuid()}${extname(file.originalname)}`) } }) }) ], controllers: [EventController], providers: [EventService] }) export class EventModule {} import { join } from 'path' //서버 프로잭트 루트 폴더 export const ROOT_PATH = process.cwd() // 외부에서 접근가능한 파일들 모아둔 폴더 이름 export const PUBLIC_FOLDER_NAME = 'upload' //위클 이벤트 사진들이 저장된 폴더 이름 export const EVENT_FOLDER_NAME = 'event' //실제 공개폴더의 실제 위치 export const PUBLIC_FOLDER_PATH = join( ROOT_PATH, PUBLIC_FOLDER_NAME ) //위클 이미지를 저장할 폴더 export const EVENT_IMG_PATH = join( PUBLIC_FOLDER_PATH, EVENT_FOLDER_NAME ) // upload/event/이미지 export const EVENT_PUBLIC_IMG_PATH = join( PUBLIC_FOLDER_NAME, EVENT_FOLDER_NAME )

  • javascript
  • typescript
  • rest-api
  • nestjs
  • backend
임상현 댓글 1 좋아요 0 조회수 209

섹션26 FileIntercepter

미해결

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

@Module({ imports: [ TypeOrmModule.forFeature([Event]), MulterModule.register({ fileFilter: (_req, file, cb) => { extname(file.originalname) return cb(null, true) }, storage: multer.diskStorage({ destination: (_req, _res, cb) => { cb(null, EVENT_IMG_PATH) }, // 파일 이름이 겹치지않게 고유한 uuid값을 이미지 고유 이름 앞에 붙여줌 filename: (_req, file, cb) => { cb(null, `${uuid()}${extname(file.originalname)}`) } }) }) ],

  • javascript
  • typescript
  • rest-api
  • nestjs
  • backend
임상현 댓글 1 좋아요 0 조회수 183

refreshToken 적용 이후

미해결

[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스

포트폴리오를 만드는중에 refreshToken을 이용해서 로그인 데이터를 저장한 후에 생긴 문제입니다. 게시글 조회 페이지에서 새로고침을 하면 fetchBoards와 fetchBoardCount 요청이 2번씩 나가고 뒤에오는 요청은 canceled 되는데 이런 경우는 어떤거 때문에 생기는건지 궁금합니다. localStorage로 되돌리면 다시 오류없이 잘 동작합니다 refreshToken을 사용하고 저 오류가 생기면 게시글 등록후에 간헐적으로 새로고침을 해야 refetch되는거 같습니다.

  • react
  • node.js
  • seo
  • graphql
  • next.js
박현민 댓글 2 좋아요 0 조회수 206

removeEventlistener 를 왜 해주어야 하는지

미해결

풀스택을 위한 탄탄한 프런트엔드 부트캠프 (HTML, CSS, 바닐라 자바스크립트 + ES6) [풀스택 Part2]

안녕하세요. 강의를 듣고 지난달부터 필요한 웹화면을 만들어서 거의 완성을 했습니다. 현재는 테스트버전으로 만든 상태이나 그럭저럭 동작들이 잘 돌아가기는 하는데 코드도 좀 지저분하다는 생각이 들고.. 여러모로 관련이 있는 강의부분들을 다시 들어보며 가다듬고 있습니다. 처음 들었을 때는 무작정 주입식으로만 생각하고 받아들였었는데... 직접 만들면서 고민을 했던 부분들이 강의내용에 있는 것들이었네요.. 역시 다시 들어볼 수록 그때는 무슨 말인지 잘 모르고 넘어갔었는데 이제사 이해가 가면서 새삼 명강의라는 걸 느낍니다. 암튼... 궁금한 게 있습니다. promise . then .catch 구문에서 promise를 생략하고 .then .catch 를 해도 돌아가기는 하는데 혹시 이러면 문제가 있을까요??

  • HTML/CSS
  • javascript
  • es6
류재안 댓글 1 좋아요 0 조회수 242

local에서는 이상없이 되는데 vercel 배포 이후에 아무것도 없을 때가 보입니다.

해결됨

한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지

현재 vercel에 정상 배포한 후 확인해보았는데 src/pages/Diary.jsx 에 존재하는 if (!curDiaryItem) { return <div>데이터 로딩중...</div>; } 이렇게 설정한 부분만 보이고 기존에 보여져야 할 것들이 모두 안보이는데 이유를 모르겠습니다 ㅠ 그리고 <meta property="op:image" content="/thumbnail.png" /> 로 지정해주었는데 왜 이것만 잘 안되는지 모르겠습니다.. 현재 vercel 주소입니다. https://emotion-diary-sable-theta.vercel.app/ github repo주소입니다. https://github.com/jjacksang/one-bite-react-v2

  • javascript
  • react
  • node.js
진상우 댓글 1 좋아요 1 조회수 717

혹시 벡엔드 서버를 따로안두고 프론트와 벡엔드 둘다 하나의 서버에 둔다면 배포방법이 달라지나요?

미해결

Next + React Query로 SNS 서비스 만들기

지금 제로초님은 벡엔드와 프론트서버를 구분해두셨고 백엔드서버는 ec2에 올리지않아서 백엔드와 관련된 것들은 작동하지 않는 상태인데, 만약에 fetch할때 백엔드서버주소가 아닌 프론트쪽 경로로 해서 하면 본 강좌에서 한것과 같이 ec2로 배포를 해도 작동을 할까요? 아니면 이경우 배포방법이 달라지나요?

  • react
  • next.js
  • react-query
  • next-auth
  • msw
김창훈 댓글 1 좋아요 0 조회수 281

게시물 팔로우 중 탭에서 게시물 업로드 오류 발생

미해결

Next + React Query로 SNS 서비스 만들기

게시물 추천 탭에서는 게시하기 버튼 클릭하면 게시물 업로드가 잘 작동됩니다. 그러나 팔로우 중 탭에서 게시하기 버튼을 클릭하면 에러가 발생합니다. 게시물 게시하기 코드는 아래와 같습니다. const mutation = useMutation({ mutationFn: async (e: React.FormEvent) =>{ e.preventDefault(); const formData = new FormData(); formData.append("content", content); imgPreview.forEach((img) => { img && formData.append("images", img.file); }); return fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts`, { method: "post", credentials: "include", body: formData, }); }, onSuccess: async (response) => { const newPost = await response.json(); setContent(""); setImgPreview([]); if (queryClient.getQueryData(["posts", "recommends"])) { queryClient.setQueryData( ["posts", "recommends"], (prev: { pages: Post[][] }) => { const shallow = { ...prev, pages: [...prev.pages] }; shallow.pages[0] = [...shallow.pages[0]]; shallow.pages[0].unshift(newPost); return shallow; } ); } if(queryClient.getQueryData(["posts", "followings"])) { console.log("get", queryClient.getQueryData(["posts", "followings"])); queryClient.setQueryData( ["posts", "followings"], (prev: { pages: Post[][] }) => { const shallow = { ...prev, pages: [...prev.pages] }; shallow.pages[0] = [...shallow.pages[0]]; shallow.pages[0].unshift(newPost); return shallow; } ); } }, onError: (error) => { console.error(error); alert("업로드 중 에러가 발생했습니다"); }, }); 최근 새소식을 보고 useSuspenseQuery 가 문제가 있다는 것을 보고 Suspense 없애고 기존에 사용한 TabDecider 컴포넌트로 변경했습니다. // src\app\(afterLogin)\home\page.tsx export default async function Home() { const session = await auth(); return ( <main className={styles.main}> <TabContextProvider> <Tab /> <PostForm me={session} /> <TabDecider /> </TabContextProvider> </main> ); } 네트워크 탭에서 posts 요청은 성공했다고 나와있습니다. 새로고침하면 업로드가 제대로 되지만 게시하기 버튼 클릭하면 onError 에서 발생하는 '업로드 중 에러 발생' 알림창이 나옵니다. react-query devtools에서 mutation 에러를 확인하니 인피니트 스크롤링하는 pages에 관한 오류가 나왔습니다. FollowingPosts 컴포넌트와 PostForm에서 queryClient.getQueryData(["posts","followings"] 에서 받는 데이터를 출력해보니 아래와 같은 데이터 구조를 가지고 있습니다. FollowingPosts 컴포넌트 코드는 다음과 같습니다. export default function FollowingPosts() { const { isPending, data } = useQuery<PostType[]>({ queryKey: ["posts", "followings"], queryFn: getFollowingPosts, staleTime: 60 * 1000, gcTime: 300 * 1000, }); if (isPending) return <Loading />; console.log("data2", data); return data?.map((post) => <Post key={post.postId} post={post} />); } FollowingPosts 컴포넌트에서는 useInifiniteQuery 가 아닌 useQuery 를 통해 데이터를 받아와서 데이터 안에 있는 pages가 없어서 이런 오류가 나오는걸까요?? 우선 useInfiniteQuery 로 변경해 게시물을 등록하면 잘 작동되는 것은 확인했습니다.

  • react
  • next.js
  • react-query
  • next-auth
  • msw
댓글 2 좋아요 0 조회수 334

next.js 멀티플 런타임 관련해서 질문 올립니다..

미해결

Next.js 시작하기(feat. 지도 서비스 개발)

안녕하세요! 강의와 직접적으로 관련된 질문은 아니지만 도저히 해결책을 도저히 찾기가 어려워서 질문 글 올립니다.. 하나의 Next.js 프로젝트에서 백엔드 api를 구성할 때, node.js와 파이썬 서버리스 함수를 함께 사용할 수 있나요? 백엔드로 파이썬 서버리스 함수를 단독으로 사용하는 것은 가능한 것 같은데, 동일한 프로젝트에서 node.js 서버리스 함수와 함께 사용할 수 있는지가 궁금합니다..

  • next.js
  • vercel
이성현 댓글 2 좋아요 1 조회수 388

next.js 멀티플 런타임 관련해서 질문 올립니다..

미해결

Next + React Query로 SNS 서비스 만들기

안녕하세요! 강의와 직접적으로 관련된 질문은 아니지만 도저히 해결책을 도저히 찾기가 어려워서 질문 글 올립니다.. 하나의 Next.js 프로젝트에서 백엔드 api를 구성할 때, node.js와 파이썬 서버리스 함수를 함께 사용할 수 있나요? 백엔드로 파이썬 서버리스 함수를 단독으로 사용하는 것은 가능한 것 같은데, 동일한 프로젝트에서 node.js 서버리스 함수와 함께 사용할 수 있는지가 궁금합니다..

  • react
  • next.js
  • react-query
  • next-auth
  • msw
이성현 댓글 1 좋아요 0 조회수 179

post하는 경우에 uid 설정

미해결

만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)

안녕하세요 강사님. 우선 강의 잘 듣고 있습니다! 강의 듣다보니 궁금한 점이 있는데 message를 post하는 경우나 reply를 post 하는 경우에,postData에 들어가는 uid를 userInfo에 있는 정보를 사용하시던데이 정보는 로그인한 유저(authUser)의 uid를 사용하는 것이 아니라 해당 홈페이지(e.g. https://blahx2.totu.dev/totuworld ) (totuwolrd의 uid)정보를 사용하는 것이 맞나요?댓글 등록시에도 마찬가지로 userInfo.uid를 uid로 사용하시더라고요.이렇게 되면 글,댓글 등록시 데이터베이스의 uid에는 로그인한 사용자의 uid가 아닌 사용자 홈페이지의 uid가 들어갈텐데 이게 맞는건지 궁금해서 여쭤봅니다! 혹시 이렇게 uid를 설정하신 이유가 로그인하지 않은 유저를 위한 것인가요?반대로 로그인을 한 유저만 익명 또는 실명으로 댓글을 남길 수 있도록 하려면 uid를 로그인한 유저의 uid로 설정해도 될까요? 아래는 강사님이 데이터를 post 하는 경우의 코드입니다.! <Button disabled={message.length === 0} //입력값이 없으면 비활성화 bgColor="#FFB86C" color="white" colorScheme="yellow" variant="solid" size="sm" onClick={async () => { const postData: { uid: string; message: string; author?: { displayName: string; photoURL?: string; }; } = { uid: userInfo.uid, message }; if (isAnonymous === false) { postData.author = { displayName: authUser?.displayName ?? 'anonymous', photoURL: authUser?.photoURL ?? '/user.png', }; } const messageResp = await postMessage(postData); if (messageResp.result === false) { toast({ title: messageResp.message, position: 'top-right' }); } setMessage(''); // 리프레쉬 글 작성하면 page를 1로 돌려 messageList를 다시 받아온다. setPage(1); setTimeout(() => { setMessageListFetchTrigger((prev) => !prev); }); }} >등록</Button>

  • next.js
여닝 댓글 1 좋아요 0 조회수 170

연결 리스트 구현 숙제 리뷰 부탁드려봅니다

해결됨

비전공자의 전공자 따라잡기 - 자료구조(with JavaScript)

// 숙제1 next가 아닌 이전것 prev를 구하기 // 숙제2 삽입이 시간복잡도가 O(n)인데 O(1)으로 변경하기 (hint tail) class Node { constructor(value) { this.value = value; this.next = null; this.prev = null; } } class LinkedList { constructor(length = 0) { this.length = length; this.head = null; this.tail = null; } add(value) { const newNode = new Node(value); if (this.head === null) { this.head = newNode; this.tail = newNode; } else { /* 수정 const [prev] = this.#search(this.length - 1); this.tail.prev = prev; this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; */ newNode.prev = this.tail; this.tail.next = newNode; this.tail = newNode; } this.length++; return this.length; } search(index) { return this.#search(index)[1]?.value; } prevSearch(index) { return this.#search(index)[0]?.value; } currentAndPrevNode(index) { return this.#search(index); } #search(index) { let count = 0; let prev; let current = this.head; while (count < index) { prev = current; current = current?.next; count++; } return [prev, current]; } remove(index) { const [prev, current] = this.#search(index); if (prev && current) { if (!current.next) { // 마지막 노드 삭제일경우! prev.next = null; // 마지막 노드 삭제일때 이전 노드의 next를 null로 만들어주면 끝 this.tail = prev; // 마지막 노드를 삭제 했기 때문에 tail을 이전 노드로 바꾸어주어야 한다. } else { // 중간 삭제 일때 prev.next = current.next; current.next.prev = prev; // 현재 노드의 next노드의 이전 노드 prev를 prev 노드로 변경 } this.length--; return this.length; } else if (current) { // index 0일 때 this.head = current.next; this.length--; return this.length; } else { // 삭제하고자 하는 대상이 없을 떄 // 아무것도 안함 } } } const linkList = new LinkedList(); linkList.add(1); // 삭제 linkList.add(2); linkList.add(3); linkList.add(4); linkList.add(5); // 삭제 linkList.add(6); linkList.add(7); linkList.add(8); // 삭제 console.log(linkList.search(6)); // 7 console.log(linkList.prevSearch(2)); // 2 console.log(linkList.tail.prev.value); // 마지막 꼬리의 이전이니까 7 console.log(linkList.remove(0)); // 첫번째 삭제 7 console.log(linkList.prevSearch(2)); // 3 console.log(linkList.tail.prev.value); // 마지막 꼬리의 이전이니까 7 console.log(linkList.remove(6)); // 마지막 삭제 6 console.log(linkList.tail.prev.value); // 삭제후 꼬리 이전이니까 6 console.log(linkList.tail.next); // null console.log(linkList.remove(3)); // 5 const [prev, cur] = linkList.currentAndPrevNode(3); // 첫번째 튜플은 이전노드 두번째 튜플은 현재 노드 console.log(prev.value); // 4 console.log(prev.next.value); // 6 console.log(cur.value); // 6 console.log(cur.prev.value); // 4 ::) prev와 tail을 추가 하였고 add 메서드와 remove 메서드를 수정 하였습니다. 질문1) 연결리스트 구현 숙제에 해당하는 정답 코드인지 리뷰 부탁드려도 될까요? 질문2) tail만 사용 했을때는 O(1) 시간복잡도를 가지게 add 메서드를 구현했었는데 prev가 추가되면서 const [prev] = this. #search (this.length - 1); 로직을 추가하여 prev를 구해서 O(1)가 아니게 된것 같은데 무언가 더 좋은 방법이 있을것 같습니다! (저는 고민 해봤는데 모르겠습니다)

  • javascript
  • 코딩-테스트
  • 알고리즘
rhkdtjd_12 댓글 2 좋아요 1 조회수 373

5장까지 강의 잘 듣고 질문있습니다.

해결됨

Supabase, Next 풀 스택 시작하기 (feat. 슈파베이스 OAuth, nextjs 14)

5장까지 너무나도 좋은 강의 감사하며 잘듣고 있습니다. 질문이 있는데요 왜 useTodoController를 TodoList 컴포넌트에서 가져와서 직접쓰지 않고 TodoContainer에서 불러와서 props로 주는건가요?? TodoList로 바로 받으면 TodoContainer도 만들 필요 없지 않나요??

  • react
  • 클론코딩
  • next.js
  • supabase
ywc0008 댓글 1 좋아요 1 조회수 346

강의교재문의

미해결

김일한의 리액트(React) 개발자를 위한 실습을 통한 입문 과정

시중 서점에 판매되는 책중에, 이 강의를 따라 배우며 같이 볼 수 있는 교재 있으면 알려주시면 좋겠습니다.

  • HTML/CSS
  • javascript
  • react
댓글 1 좋아요 0 조회수 189

relation Selection 관련

미해결

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

Image 의 특정 컬럼만 select 하고 싶어서 find 옵션에 아래와 비슷하게 적용했더니, transform 데코레이터가 적용이 되지 않네요. chatGPT 에게 물어보니 transform 데코레이터 적용을 받으려면 QueryBuilder 를 쓰라는데, 가독성이 떨어질 것 같아서 찜찜하네요. 다른 방법은 없을까요? 아니면 일단 그냥 인강대로 쭉 진행한 다음 Prisma 로 빠르게 마이그레이션 한 다음에 이런 고민을 진행하는 게 나으려나요? export const POST_FIND_OPTIONS: FindManyOptions<PostsModel> = { select: { id: true, ..., images: { path: true } }, relations: ['images', 'author'], };

  • javascript
  • typescript
  • rest-api
  • nestjs
  • backend
sangyun lee 댓글 1 좋아요 0 조회수 208

public/posts가 붙는이유

해결됨

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

좋은 강의 감사합니다! DB에는 파일명만 저장되어있고 post POST 요청보낼때도 파일명만 보내는데, 어떻게 post get요청을할때 public/posts가 붙는건가요?

  • javascript
  • typescript
  • rest-api
  • nestjs
  • backend
이북자 댓글 1 좋아요 1 조회수 251

인기 태그

인프런 TOP Writers

주간 인기글