React

 

따라하며 배우는 리액트 A-Z

 


[프론트엔드, 웹 개발] 강의입니다.

이 강의를 통해 리액트 기초부터 중급까지 배우게 됩니다. 하나의 강의로 개념도 익히고 실습도 하며, 리액트를 위해 필요한 대부분의 지식을 한번에 습득할 수 있도록 만들었습니다.

✍️
이런 걸
배워요!

리액트

NextJS

타입스크립트

정적 사이트 자동 배포

도커

 

강의:  https://www.inflearn.com/course/%EB%94%B0%EB%9D%BC%ED%95%98%EB%8A%94-%EB%A6%AC%EC%95%A1%ED%8A%B8#

 

강의 자료 :  https://github.com/braverokmc79/DiagramPDF

 

소스 : 

https://github.dev/braverokmc79/react-nextjs13-app

 

 

 

 

 

[8].  Nextjs 13

 

 

92. Nextjs 13

 

강의:

https://www.inflearn.com/course/따라하는-리액트/unit/135794?tab=curriculum

 

 

디렉토리 생성

 

react-nextjs13-app

 

 

 

nextjs 설치
 

$ npx create-next-app@latest ./ --typescript
√ Would you like to use ESLint with this project? ... No / Yes
√ Would you like to use `src/` directory with this project? ... No / Yes
√ Would you like to use experimental `app/` directory with this project? ... No / Yes
√ What import alias would you like configured? ... @/*
Creating a new Next.js app in F:\Study\React\nextjs-app.
 
Using npm.
 
Installing dependencies:
- react
- react-dom
- next
- @next/font
- typescript
- @types/react
- @types/node
- @types/react-dom
- eslint
- eslint-config-next




 

 

 

 

next.config.js

 

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify:true,
  experimental:{appDir:true}
}

module.exports = nextConfig

 

 

 

app/pages.js

import React from 'react'

export default function page() {
  return (
    <div>메인 페이지</div>
  )
}

 

 

app/layout.tsx

import Link from "next/link"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <head />
      <body>
         <nav>
            <Link  href="/">
                Home
            </Link>&nbsp;
            <Link  href="/posts/ ">
                Post
            </Link>
         </nav>

         <main>
         {children}
         </main>
       
        
        </body>
    </html>
  )
}

 

 

 

app/head.tsx

export default function Head() {
  return (
    <>
      <title></title>
      <meta content="width=device-width, initial-scale=1" name="viewport" />
      <link rel="icon" href="/favicon.ico" />
    </>
  )
}

 

 

 

 

 

app/posts/pages.js

import Link from "next/link";
import CreatePost from "./CreatePost";

async function  getPost(){
  const res =await fetch("http://127.0.0.1:8090/api/collections/posts/records", {cache : "no-store"}) ;
 
  const data =await res.json();
  //console.log("data  getPost : " , data);
  return data?.items  as  any[];
}

const  PostPage =async()=> {
  const posts =await getPost();
  //console.log("posts  : " , posts);
  return (
    <div>
      <h1>Posts</h1>
      {posts?.map((post)=>{
          return <PostItem  key={post.id}  post={post}   />
      })}


       <CreatePost />      
    </div>
  )
}

export default PostPage;


const PostItem = ({post} : any)=>{
  const {id , title, created } = post ||  {};

  return (
    <div>
        <Link href={`/posts/${id}`}>
            <div>
                <h3>
                  {title}
                </h3>
                <p>{created}</p>
            </div>
          </Link>  

      
     </div>

  )
}

 

 

 

app/posts/CreatePost.tsx

'use client';

import { useState } from "react";
import { useRouter } from 'next/navigation';

const CreatePost = () => {
    const [title, setTitle] = useState('');
    const router = useRouter();

    const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        await fetch('http://127.0.0.1:8090/api/collections/posts/records', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                title
            })
        })
        setTitle('');
        // Refresh the current route and fetch new data from the server
        router.refresh();

    }
    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                placeholder="Title"
                value={title}
                onChange={(e) => setTitle(e.target.value)}
            />
            <button type="submit">
                Create post
            </button>
        </form>
    )
}

export default CreatePost

 

 

 

 

app/posts/[id]/page.tsx

async function getPost(postId :string){
    const res =await fetch(`http://127.0.0.1:8090/api/collections/posts/records/${postId}`, {next: {revalidate:10}});

    if(!res.ok){
        //가장 가까이에 있는 error.js actived
        throw new Error('Failed to fetch data');
    }

    const data =res.json();
    return data;
}


const PostDetailPage = async ({params} : any) => {
  const post =await getPost(params.id);

  return (
    <div>
        <h1>posts/{post.id}</h1>
        <div>
            <h3>{post.title}</h3>
            <p>{post.created}</p>
        </div>
    </div>
  )
}

export default PostDetailPage

 

 

 

 

app/posts/[id]/error.tsx

'use client';

import { useEffect } from 'react';

export default function Error({
    error,
    reset,
}: {
    error: Error;
    reset: () => void;
}) {
    useEffect(() => {
        // Log the error to an error reporting service
        console.error(error);
    }, [error]);

    return (
        <div>
            <p>Something went wrong!</p>
            <button onClick={() => reset()}>Reset error boundary</button>
        </div>
    );
}

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

옛날 한신(韓信)은 뜻을 얻어 과거에 자기가 불우할 때에 곤궁을 구해준 빨래하는 노파에게 보은을 했다. 지금 그 노파와 같은 당신의 은혜에 대해 나는 고맙기 그지없이 느끼고는 있으나 부끄러운 일이지만 한신과 같은 재주가 없으니 한신처럼 그 은혜를 갚을 수 있을지 모르겠다. -고시원

댓글 ( 4)

댓글 남기기

작성