컴퓨터/nextjs

next.js 설치

풍경소리^^ 2023. 11. 29. 12:22

https://www.youtube.com/watch?v=smAU6-ZdcoQ&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=2

폴더 만든 후

폴더 안으로 이동해서

npx create-next-app@latest . (마침표)

npm notice 
npm notice New patch version of npm available! 10.2.3 -> 10.2.4
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.2.4
npm notice Run npm install -g npm@10.2.4 to update!
npm notice 
npm ERR! code ENOENT
npm ERR! syscall lstat
npm ERR! path C:\Users\newstep\AppData\Roaming\npm
npm ERR! errno -4058
npm ERR! enoent ENOENT: no such file or directory, lstat 'C:\Users\newstep\AppData\Roaming\npm'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! A complete log of this run can be found in: C:\Users\newstep\AppData\Local\npm-cache\_logs\2023-11-29T03_08_56_789Z-debug-0.log

npm install -g npm@10.2.4

npx create-next-app@latest . (마침표)

 

Ok to proceed? (y) y

? Would you like to use TypeScript? » No / Yes 노 엔터

√ Would you like to use ESLint? ... No / Yes 예스 엔터
√ Would you like to use Tailwind CSS? ... No / Yes  예스 엔터
√ Would you like to use `src/` directory? ... No / Yes  예스 엔터
? Would you like to use App Router? (recommended) » No / Yes  예스 엔터

? Would you like to customize the default import alias (@/*)? » No / Yes 노 엔터

 

설치 완료 후

 

개발환경 실행

npm run dev

실행되면

크롬으로 http://localhost:3000

접속하면 됨

 

https://www.youtube.com/watch?v=siBQ-y84ZO0&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=3

src\app\layout.js--------------------샘플 웹페이지 리모델링

import './globals.css'

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({ children }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}

src\app\page.js --------------------children

import Image from 'next/image'

export default function Home() {
  return (
    <>Hello 안녕, Next.js</>
  )
}

src\app\globals.css --------------------내용지우기

 

https://www.youtube.com/watch?v=WuhJN-_pWfI&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=4

 

package.json-------------------- 

{
  "name": "next",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^18",
    "react-dom": "^18",
    "next": "14.0.3"
  },
  "devDependencies": {
    "autoprefixer": "^10.0.1",
    "postcss": "^8",
    "tailwindcss": "^3.3.0",
    "eslint": "^8",
    "eslint-config-next": "14.0.3"
  }
}

node.js 기반 프로젝트들은 

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  }

Scripts 안에 프로젝트를 유지 보수 하기위한 여러가지 명령들을 갖고 있다

npm run dev 라고 하면

"dev": "next dev"
 

가 실행 되는 것이다

 

실서버를 위한 배포 명령은

"build": "next build"

배포판을 서비스 시작하는 명령은

"start": "next start"

서버 끄기 Ctrl+C

 

배포명령

npm run build

.next폴더에 배포판이 만들어진다

 

배포판 실행하려면

npm run start

크롬으로 http://localhost:3000

접속하면 됨 (네트워크 탭에서 전송된 데이터량을 보면 엄청 줄어든 것을 알 수 있다)

 

https://www.youtube.com/watch?v=4xEw3Nz5hTY&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=5

서버 끄기 Ctrl+C

 

개발서버 실행

npm run dev

src\app\page.js --------------------

import Image from 'next/image'

export default function Home() {
  return (
    <>
      <h2>Welcome</h2>
      Hello, WEB!
    </>
  )
}

src\app\layout.js --------------------

import './globals.css'

export const metadata = {
  title: 'Web tutorials',
  description: 'Generated by egoing',
}

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <h1><a href='/'>WEB</a></h1>
        <ol>
          <li><a href='/read/1'>html</a></li>
          <li><a href='/read/2'>css</a></li>
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><a href='/create'>Create</a></li>
          <li><a href='/update/1'>Update</a></li>
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

 

https://www.youtube.com/watch?v=xE-GT8gjgJw&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=6

src\app\create\page.js--------------------폴더 만들고 파일도 만들자

export default function Create(){
    return (
        <>Create!!</>
    )
}

src\app\create\layout.js--------------------

export default function Layout(props){
    return (
        <form>
            <h2>Create</h2>
            <hr />
            <hr />
            {props.children}
            <hr />
            <hr />
        </form>
    )
}

다이나믹 라우터

src\app\read\[id]\page.js --------------------

export default function Read(props){
    return (
        <>
            <h2>Read!!</h2>
            parameters : {props.params.id}
        </>
    )
}

 

https://www.youtube.com/watch?v=en-OkqU-agI&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=7

enable javascript

 

src\app\layout.js --------------------

import Link from 'next/link'
import './globals.css'

export const metadata = {
  title: 'Web tutorials',
  description: 'Generated by egoing',
}

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1> {/* 수정 */} 
        <ol>
          <li><Link href='/read/1'>html</Link></li> {/* 수정 */}
          <li><Link href='/read/2'>css</Link></li> {/* 수정 */}
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li> {/* 수정 */}
          <li><Link href='/update/1'>Update</Link></li> {/* 수정 */}
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

 

https://www.youtube.com/watch?v=Ypkm0s69FTM&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=8

public폴더에 hello.png 넣기

src\app\page.js --------------------

import Image from 'next/image'

export default function Home() {
  return (
    <>
      <h2>Welcome</h2>
      Hello, WEB!
      <br />
      <img src='/hello.png'></img>
    </>
  )
}

 

https://www.youtube.com/watch?v=9TdWS8fmcRo&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=9

src\app\globals.css --------------------

h1 a {
    text-decoration: none;
}

 

https://www.youtube.com/watch?v=WhTB0q1nvDM&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=10

npx json-server --port 9999 --watch db.json

 

db.json --------------------

{
  "posts": [
    {
      "id": 1,
      "title": "json-server",
      "author": "typicode"
    }
  ],
  "comments": [
    {
      "id": 1,
      "body": "some comment",
      "postId": 1
    }
  ],
  "profile": {
    "name": "typicode"
  }
}

 

 

크롬으로 http://localhost:9999/posts 접속하면 아래와 같이 리턴됨

// 20231129161153
// http://localhost:9999/posts

[
  {
    "id": 1,
    "title": "json-server",
    "author": "typicode"
  }
]

 

db.json -------------------- 수정

{
  "topics": [
    {"id": 1, "title": "html", "body": "html is ..."},
    {"id": 2, "title": "css", "body": "css is ..."}
  ],
  "posts": [
    {
      "id": 1,
      "title": "json-server",
      "author": "typicode"
    }
  ],
  "comments": [
    {
      "id": 1,
      "body": "some comment",
      "postId": 1
    }
  ],
  "profile": {
    "name": "typicode"
  }
}

크롬으로  http://localhost:9999/topics 접속하면 아래와 같이 리턴됨

// 20231129161746
// http://localhost:9999/topics

[
  {
    "id": 1,
    "title": "html",
    "body": "html is ..."
  },
  {
    "id": 2,
    "title": "css",
    "body": "css is ..."
  }
]

크롬에서 개발자도구-네트워크

esc 클릭

console창 클릭 (서버 통신에 사용하는 fetch 명령으로 json 데이터 가져오자)

fetch('http://localhost:9999/topics')
  .then((resp)=>{
    return resp.json();
  })
  .then((result)=>{
    console.log('result', result);
  });

결과

 

https://www.youtube.com/watch?v=t-Jt961oNeg&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=11

 

클라이언트로 작업하면 비효율적

서버에서 데이터를 가져오기 위해서는 useEffect()가 필요

한 번만 실행하게 빈 배열을 넣는다

layout.js --------------------

import Link from 'next/link'
import './globals.css'
import { useEffect, useState } from 'react'

export const metadata = {
  title: 'Web tutorials',
  description: 'Generated by egoing',
}

export default function RootLayout({ children }) {
  const [topics, setTopics] = useState([]);{/* 빈 배열 */}
  useEffect(()=>{
    fetch('http://localhost:9999/topics')
      .then(resp=>resp.json())
      .then(result=>{
        setTopics(result);
      });
  },[]);
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1> {/* 수정 */} 
        <ol>
          <li><Link href='/read/1'>html</Link></li> {/* 수정 */}
          <li><Link href='/read/2'>css</Link></li> {/* 수정 */}
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li> {/* 수정 */}
          <li><Link href='/update/1'>Update</Link></li> {/* 수정 */}
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

 

layout.js --------------------

"use client";
import Link from 'next/link'
import './globals.css'
import { useEffect, useState } from 'react'

export const metadata = {
  title: 'Web tutorials',
  description: 'Generated by egoing',
}

export default function RootLayout({ children }) {
  const [topics, setTopics] = useState([]);{/* 빈 배열 */}
  useEffect(()=>{
    fetch('http://localhost:9999/topics')
      .then(resp=>resp.json())
      .then(result=>{
        setTopics(result);
      });
  },[]);
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1> {/* 수정 */} 
        <ol>
          <li><Link href='/read/1'>html</Link></li> {/* 수정 */}
          <li><Link href='/read/2'>css</Link></li> {/* 수정 */}
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li> {/* 수정 */}
          <li><Link href='/update/1'>Update</Link></li> {/* 수정 */}
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

layout.js --------------------

"use client";
import Link from 'next/link'
import './globals.css'
import { useEffect, useState } from 'react'

// export const metadata = {
//   title: 'Web tutorials',
//   description: 'Generated by egoing',
// }

export default function RootLayout({ children }) {
  const [topics, setTopics] = useState([]);{/* 빈 배열 */}
  useEffect(()=>{
    fetch('http://localhost:9999/topics')
      .then(resp=>resp.json())
      .then(result=>{
        setTopics(result);
      });
  },[]);
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1> {/* 수정 */} 
        <ol>
          <li><Link href='/read/1'>html</Link></li> {/* 수정 */}
          <li><Link href='/read/2'>css</Link></li> {/* 수정 */}
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li> {/* 수정 */}
          <li><Link href='/update/1'>Update</Link></li> {/* 수정 */}
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

layout.js --------------------

"use client";
import Link from 'next/link'
import './globals.css'
import { useEffect, useState } from 'react'

// export const metadata = {
//   title: 'Web tutorials',
//   description: 'Generated by egoing',
// }

export default function RootLayout({ children }) {
  const [topics, setTopics] = useState([]);{/* 빈 배열 */}
  useEffect(()=>{
    fetch('http://localhost:9999/topics')
      .then(resp=>resp.json())
      .then(result=>{
        setTopics(result);
      });
  },[]);
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1>
        <ol>
          <li><Link href='/read/1'>html</Link></li>
          <li><Link href='/read/2'>css</Link></li>
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li>
          <li><Link href='/update/1'>Update</Link></li>
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

topics로 글목록 만들기

npx json-server --port 9999 --watch db.json
npm run dev

 

아쉬운 점들

서버가 원격에 있다면 데이터를 가져오는데 시간이 걸린다

useEffect(()=>{
    fetch('http://localhost:9999/topics')
      .then(resp=>resp.json())
      .then(result=>{
        setTopics(result);
      });
  },[]);

 

서버컨퍼넌트로 바꾸려면

layout.js --------------------

import Link from 'next/link'
import './globals.css'
import { useEffect, useState } from 'react'

// export const metadata = {
//   title: 'Web tutorials',
//   description: 'Generated by egoing',
// }

export default function RootLayout({ children }) {
  const [topics, setTopics] = useState([]);{/* 빈 배열 */}
  useEffect(()=>{
    fetch('http://localhost:9999/topics')
      .then(resp=>resp.json())
      .then(result=>{
        setTopics(result);
      });
  },[]);
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1>
        <ol>
          {topics.map((topic)=>{
            return <li key={topic.id}><Link href={`/read/${topic.id}`}>{topic.title}</Link></li>
          })}
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li>
          <li><Link href='/update/1'>Update</Link></li>
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

"use client" 없으면 되는데 

여기에서 에러나는 이유는 useState, useEffect 사용해서 그렇다

layout.js --------------------

import Link from 'next/link'
import './globals.css'

export const metadata = {
  title: 'Web tutorials',
  description: 'Generated by egoing',
}

export default async function RootLayout({ children }) {
  const resp = await fetch('http://localhost:9999/topics');
  const topics = await resp.json();
  return (
    <html>
      <body>
        <h1><Link href='/'>WEB</Link></h1>
        <ol>
          {topics.map((topic)=>{
            return <li key={topic.id}><Link href={`/read/${topic.id}`}>{topic.title}</Link></li>
          })}
        </ol>
        <hr />
        {children}
        <hr />
        <ul>
          <li><Link href='/create'>Create</Link></li>
          <li><Link href='/update/1'>Update</Link></li>
          <li><input type='button' value='delete' /></li>
        </ul>
      </body>
    </html>
  )
}

 

https://www.youtube.com/watch?v=nHCBiiTYma4&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=12

 

src\app\read\[id]\page.js --------------------

export default async function Read(props){
    const resp = await fetch(`http://localhost:9999/topics/${props.params.id}`)
    const topic = await resp.json();
    return (
        <>
            <h2>{topic.title}</h2>
            {topic.body}
        </>
    )
}

 

https://www.youtube.com/watch?v=esmAbm-UyB4&list=PLuHgQVnccGMCwxXsQuEoG-JJ7RlwtNdwJ&index=13

 

src\app\create\layout.js 필요 없으니까 지우자

 

혹시 에러날 경우

rm -rf .next
npm run dev

하면 새로 만들어줌

 

submit 누르면 페이지전환 되는데 이를 방지하기위해서

e.preventDefault();

 

title과 body 값 알아내기

 

src\app\create\page.js --------------------

"use client"

import { useRouter } from "next/navigation";

export default function Create(){
    const router = useRouter();
    return (
        <form onSubmit={(e)=>{
            e.preventDefault();
            const title = e.target.title.value;
            const body = e.target.body.value;
            const options = {
                method: 'POST',
                Headers: {
                    'Content-Type': 'application/json'
                },
                body : JSON.stringify({title, body})
            }
            fetch(`http://localhost:9999/topics`, options)
                .then(res=>res.json())
                .then(result=>{
                    console.log(result);
                    const lastid = result.id;
                    router.push(`/read/${lastid}`);
                })
        }}>
            <p>
                <input type="text" name="title" placeholder="title" />
            </p>
            <p>
                <textarea name="body" placeholder="body"></textarea>
            </p>
            <p>
                <input type="submit" value="create" />
            </p>
        </form>
    )
}

http://localhost:3000/create

데이터 입력

javascript

create 버튼 클릭

react

create 버튼 클릭

'컴퓨터 > nextjs' 카테고리의 다른 글

nextjs-app 만들기  (0) 2025.02.19