728x90
반응형

* Next.js에 Tailwind css를 적용시킨 후 따라해주세요.

 

1.  next-themes 설치

# NPM
npm install next-themes

# Yarn 
yarn add next-themes

 

 

 

2.  Root Layout에  ThemeProvider 추가

import "@/styles/globals.css";
import { ThemeProvider } from 'next-themes';

...

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html suppressHydrationWarning>
      <body>
        <ThemeProvider attribute='class'>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

 

- ThemeProvider가 children을 감싸도록 작성합니다.

- 경고 표시를 무시하기 위해 html 태그에 suppressHydrationWarning를 입력합니다.

- 추가적인 설정이 없다면 system이 기본 테마로 설정됩니다.

 

 

 

3.  tailwind.config.js에  다크모드 설정 추가

// tailwind.config.ts

import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      backgroundImage: {
        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
        "gradient-conic":
          "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
      },
    },
  },
  darkMode: 'class', // 다크 모드를 클래스 기반으로 설정
  plugins: [],
};
export default config;

 

darkMode: 'class' 를 추가하여 클래스 기반으로 다크모드일때의 스타일을 지정할 수 있도록 설정합니다.

 

 

 

4.  다크모드 스타일 지정

export default function Page() {

  return (
    <div className="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100">
    	기본인 light 모드에서는 text-gray-600의 색상에서 hover시 text-gray-900으로 색이 변경되며 더욱 진해집니다.
        dartk 모드에서는 text-gray-300의 색상에서 hover시 text-gray-100으로 색이 변경되며 더욱 밝아집니다.
    </div>
  );
}

 

 

 

 

 

5.  테마 변경 컴포넌트

import { useState } from "react";
import { useTheme } from "next-themes";
import LightModeOutlinedIcon from '@mui/icons-material/LightModeOutlined';
import BedtimeOutlinedIcon from '@mui/icons-material/BedtimeOutlined';
import MonitorIcon from '@mui/icons-material/Monitor';

const ThemeSelector: React.FC = () => {
  const { theme, setTheme } = useTheme();
  const [themeToggle, setThemeToggle] = useState(false);

  const themeShowToggle = () => {
    setThemeToggle(!themeToggle);
  };

  const handleChange = (themeValue: string) => {
    setTheme(themeValue);
    setThemeToggle(false);
  };

  return (
    <div className="relative">
        <div className="text-blue-500 mr-6 cursor-pointer" onClick={themeShowToggle}>
          {theme === 'light'
            ? <LightModeOutlinedIcon id="theme_light"/>
            : theme === 'dark'
            ? <BedtimeOutlinedIcon id="theme_dark"/>
            : <MonitorIcon id="theme_system"/>}
        </div>
        {themeToggle &&
          <div className="absolute top-16 right-4 w-28 bg-white shadow-lg py-1 rounded-md border-solid border border-gray-300 text-gray-900 font-medium text-sm dark:text-gray-100 dark:bg-backDarkColor">
            <div className={["flex flex-row cursor-pointer px-3 py-1 hover:bg-gray-100 dark:hover:bg-zinc-800", theme === 'light' && "text-blue-500"].join(' ')} onClick={() => handleChange('light')}>
              <LightModeOutlinedIcon className={[theme === 'light'?"text-blue-500":"text-gray-400", "w-5 mr-2"].join(' ')}/>
              Light
            </div>
            <div className={["flex flex-row cursor-pointer px-3 py-1 hover:bg-gray-100 dark:hover:bg-zinc-800", theme === 'dark' && "text-blue-500"].join(' ')} onClick={() => handleChange('dark')}>
              <BedtimeOutlinedIcon className={[theme === 'dark'?"text-blue-500":"text-gray-400", "w-5 mr-2"].join(' ')}/>
              Dark
            </div>
            <div className={["flex flex-row cursor-pointer px-3 py-1 hover:bg-gray-100 dark:hover:bg-zinc-800", theme === 'system' && "text-blue-500"].join(' ')} onClick={() => handleChange('system')}>
              <MonitorIcon className={[theme === 'system'?"text-blue-500":"text-gray-400", "w-5 mr-2"].join(' ')}/>
              System
            </div>
          </div>
        }
    </div>
  );
};

export default ThemeSelector;

 

* MUI(Material UI) 아이콘이 사용된 코드입니다.

* 다듬어지지 않은 예시 코드입니다. 실제로 사용시에는 새로 만들거나 다듬어서 사용하시길 추천드립니다.

 

- useTheme()를 통해 현재 설정된 theme를 불러올 수 있고, setTheme를 사용하여 테마 설정을 변경할 수 있습니다.

   (system, light, dark 등으로 설정)

 

 

 

 

 

 

[next-themes 공식문서]

https://github.com/pacocoursey/next-themes

 

GitHub - pacocoursey/next-themes: Perfect Next.js dark mode in 2 lines of code. Support System preference and any other theme wi

Perfect Next.js dark mode in 2 lines of code. Support System preference and any other theme with no flashing - pacocoursey/next-themes

github.com

728x90
반응형
728x90
반응형

* git 연동 후 vercel에 등록된 상태로 진행해주세요*

 

1.  Postgres Database 생성

 

Storage -> Create Database 클릭

 

 

 

 

Postgres -> Database 설정

 

- Database Name : 데이터베이스명을 입력합니다.

- Region : 데이터센터의 물리적 위치를 선택합니다. (프로그램이 실행되는 위치와 가까운 장소를 선택할수록 빠른 통신이 가능합니다)

 

 

 

 

2. 프로젝트에 DB 연결

 

생성된 postgres database로 접속후 -> Getting Started -> Connect Project 클릭

 

 

 

 

vercel에 연결되어있는 프로젝트 중 DB에 연결할 프로젝트를 선택합니다.

 

저는 기본설정으로 연결하였습니다.

 

 

 

 

vercel link

vercel env pull .env.development.local

 

vscode로 프로젝트에 접속하여 로컬에서도 postgres를 사용할 수 있도록 설정합니다.

 

* .env.development.local 파일에는 postgres 관련 민감한 정보들이 들어있으니

    git 또는 외부에 공유되지 않도록 주의해야합니다.

 

 

 

 

3. 테이블 생성 및 데이터 추가

 

vercel -> Storage -> 생성한 postgres -> Data -> Query 로 접근

 

아래 쿼리들을 실행하여 데이터를 넣고 테스트 해봅시다.

 

 

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255),
    content TEXT,
    author VARCHAR(100)
);

 

해당 쿼리는 게시글 테이블을 생성합니다.

 

INSERT INTO posts (title, content, author) VALUES
    ('첫 번째 게시물', '첫 번째 게시물의 내용입니다.', '홍길동'),
    ('두 번째 게시물', '두 번째 게시물의 내용입니다.', '이순신'),
    ('세 번째 게시물', '세 번째 게시물의 내용입니다.', '강감찬'),
    ('네 번째 게시물', '네 번째 게시물의 내용입니다.', '유관순'),
    ('다섯 번째 게시물', '다섯 번째 게시물의 내용입니다.', '김유신');

 

해당 쿼리는 게시글 테이블에 데이터를 넣습니다.

 

 

 

데이터가 잘 들어왔다면 프로젝트에서 조회해봅시다.

 

 

 

 

4. 데이터 조회하기

npm install @vercel/postgres

 

postgres 데이터를 조회할 수 있는 라이브러리를 설치합니다.

 

 

 

/* /services/posts.ts */

'use server'

import { sql } from "@vercel/postgres";

export interface Posts {
	id : number,
	title : string,
	content : string,
	author : string,
}

export async function getPostsData(): Promise<Posts[]> {
	try {
		const { rows }: { rows: Posts[] } = await sql
			`SELECT id, title, content, author FROM posts;`;
		return rows;
	} catch (error) {
		throw new Error('Failed to fetch posts data');
	}
}

 

조회 쿼리를 통해 직접 게시글 목록을 조회하는 파일입니다.

 

 

/* page.tsx */

'use client'
import { Posts, getPostsData } from "@/services/posts";
import { useEffect, useState } from "react";

const defaultPosts: Posts = {
  id: 0,
  title: "",
  content: "",
  author: "",
};

export default function Page() {
  const [posts,getPosts] = useState<Posts[]>([defaultPosts]);

  useEffect(() => {
    fetchData();
  },[]);

  const fetchData = async () => {
    try {
      const postsData = await getPostsData();
      getPosts(postsData);
    } catch (error) {
      throw new Error('Failed to fetch posts data');
    }
  };

  return (
    <div>
      <div className="overflow-x-auto">
        <table className="min-w-full bg-white">
          <thead>
            <tr>
              <th className="px-6 py-3 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-600 uppercase tracking-wider">제목</th>
              <th className="px-6 py-3 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-600 uppercase tracking-wider">내용</th>
              <th className="px-6 py-3 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-600 uppercase tracking-wider">작성자</th>
            </tr>
          </thead>
          <tbody className="divide-y divide-gray-200">
            {posts.map((post)=>(
              <tr key={post.id}>
                <td className="px-6 py-4 whitespace-no-wrap">{post.title}</td>
                <td className="px-6 py-4 whitespace-no-wrap">{post.content}</td>
                <td className="px-6 py-4 whitespace-no-wrap">{post.author}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

 

조회된 데이터가 테이블에 출력될 수 있도록 처리합니다.

 

 

 

 

 

postgres에 쿼리로 넣었던 데이터가 정상적으로 조회되는것을 볼 수 있습니다.

 

 

 

 

DROP TABLE posts;

 

마지막으로 테스트용 테이블인 posts를 삭제하고 프로젝트에 맞게 사용하시면 됩니다.

728x90
반응형
728x90
반응형

1.  SVRG 설치

# NPM
npm install --save-dev @svgr/webpack

# Yarn 
yarn add --dev @svgr/webpack

 

 

 

2. next.config.js에  웹팩 구성 추가

[next.config.js에 추가하는 경우]

module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/i,
      issuer: /\.[jt]sx?$/,
      use: ['@svgr/webpack'],
    })

    return config
  },
}

 

[next.config.mjs에 추가하는 경우]

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/i,
      issuer: /\.[jt]sx?$/,
      use: ['@svgr/webpack'],
    })

    return config
  },
};

export default nextConfig;

 

웹팩 구성으로 위의 코드를 추가하지 않으면 아래와 같은 오류가 발생합니다.

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

 

 

 

3. svg 이미지를 컴포넌트로 import


import Note from '@public/svgs/note.svg';

const TodoList: React.FC = () => {

  return (
    <>
      <Note/>
    </>
  );
};

export default TodoList;

 

 

 

*svg 관련해서 폴더 구조 잡을때 아래 글도 참고하기 좋았습니다. 추천드립니다.

Next.js 폴더/파일 구조 잡기 (miriya.net)

 

Next.js 폴더/파일 구조 잡기

주니어 개발자들이 가장 많이 물어보는 질문이 폴더 구조에 대해 묻는 것이다. 사실 이런 질문들은 항상 답이 정해져있다. “그때 그때 달라요” 아니 시발 그때 그때 다른게 어딨어? 하고 빡이

miriya.net

* 공식문서

https://blog.logrocket.com/import-svgs-next-js-apps/

 

How to import SVGs into your Next.js apps - LogRocket Blog

This article will explore the different methods you can use to import and use SVGs in a Next.js application.

blog.logrocket.com

 

728x90
반응형
728x90
반응형

사이드 프로젝트를 진행하던 중 코드블럭을 개발하게 되어서

코드에 하이라이트를 적용하기 위해 react-syntax-highlighter 라이브러리를 사용하였습니다.

 

1.  react-syntax-highlighter 설치

npm install react-syntax-highlighter

 

Typescript를 사용하시는 경우 아래 명령어도 실행해주세요.

npm install --save-dev @types/react-syntax-highlighter

 

 

2. 사용할 스타일 선택

https://react-syntax-highlighter.github.io/react-syntax-highlighter/demo/prism.html

 

React Syntax Highlighter Demo

 

react-syntax-highlighter.github.io

라이브러리 데모 페이지

 

데모 페이지에서 언어와 스타일을 바꿔가며 적용될 스타일을 미리 볼 수 있습니다.

 

 

 

3. 코드블럭 컴포넌트에 스타일 적용

'use client'

import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { solarizedlight } from 'react-syntax-highlighter/dist/esm/styles/prism';

const CodeBlock: React.FC<{ code: string }> = ({code}) => {

  return (
    <>
      <div className="w-full">
        <div className="bg-blue-100 w-full h-9 rounded-t-lg px-4 py-2 text-sm text-gray-500 font-medium">index.js</div>
        <div className="bg-blue-50 w-full h-20 rounded-b-lg px-4 py-2 text-sm text-gray-800 font-medium">
          <SyntaxHighlighter language={"javascript"} style={solarizedlight}>
            {code}
          </SyntaxHighlighter>
        </div>
      </div>
    </>
  );
};

export default CodeBlock;

 

저는 solarizedlight 스타일의 하이라이트를 사용하였습니다.

 

코드블럭에서 코드가 실제로 출력되는 부분을 SyntaxHighlighter 태그로 감싸줍니다.

 

SyntaxHighlighter 태그에 코드를 구성하는 언어 정보와 스타일 정보를 props로 넘겨줍니다.

스타일 정보를 넘기지 않으면 기본 스타일로 적용됩니다.

 

 

4. 결과

기존 코드 블럭
필자가 선택한 하이라이트 스타일 적용
기본 하이라이트 스타일에서 배경색 제거

 

직접 선택한 스타일은 노란 배경색을 벗겨서 사용해봤는데 베이스가 회색이라 그런지 코드가 흐릿하게 보였습니다.

개인적으로 기본으로 제공하는 스타일이 제일 깔끔하고 좋았습니다.

728x90
반응형
728x90
반응형

1.  Node.js 설치

https://nodejs.org/en/download

 

Node.js — Download

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

 

 

2. Visual Studio Code 설치

* os에 맞게 잘 선택해서 설치하시길 바랍니다.

https://code.visualstudio.com/

 

Visual Studio Code - Code Editing. Redefined

Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.  Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.

code.visualstudio.com

 

 

3. Visual Studio Code에서 프로젝트 관리 폴더 열기

파일 > 열기 > 미리 생성해둔 폴더 열기

visual studio code에서 quickjs 폴더가 열린 상태

 

 

 

4.  터미널에 프로젝트 생성 명령어 입력

터미널 > 새 터미널 > 열린 터미널에 명령어 입력

npx create-next-app@latest .

설치시 본인이 개발할 프로젝트에 맞게 답변

 

 

5. 프로젝트 실행

생성된 프로젝트 폴더로 다시 이동합니다. ( 파일 > 열기 > 방금 생성된 프로젝트 폴더 )

 

터미널을 다시 열고 실행 명령어를 입력합니다.

npm run dev

 

127.0.0.1:3000 주소로 프로젝트가 열립니다. (localhost:3000도 동일한 주소라고 생각하시면 됩니다)

로컬에서 프로젝트 실행

 

 

Would you like to run the app on another port instead? (Y/n)

위와 같은 문구가 뜬 경우에는 프로젝트에 설정된 실행 포트번호를 사용하고 있는 프로젝트가 있다는 뜻입니다.

설정 변경이 없었다면, 3000번 포트번호를 사용중인 프로젝트가 있을 수 있습니다.

 

Y 입력시 : 프로젝트 실행 포트번호가 아닌 다른 포트번호로 프로젝트를 실행시켜줍니다.

n 입력시 : 취소

 

 

6. 프로젝트 종료

프로젝트를 종료하고 싶다면 터미널에 Ctrl+C를 입력하시면 됩니다.

 

 

 

 

728x90
반응형

+ Recent posts