체인의정석

[React+Next+typescript+rainbow wallet] 관리자 페이지 만들고 rainbow wallet 붙이기 (블록체인 앱 관리자 페이지 만들기) 본문

개발/frontend

[React+Next+typescript+rainbow wallet] 관리자 페이지 만들고 rainbow wallet 붙이기 (블록체인 앱 관리자 페이지 만들기)

체인의정석 2025. 3. 27. 13:47
728x90
반응형

1. 관리자 페이지 기본 템플릿다운로드 순서


1. 템플릿
https://tailadmin.com/download

 

Download Free Tailwind Admin Template - TailAdmin

Download TailAdmin Now Select your preferred option below to start Download and Kickstart your journey.

tailadmin.com

2. 버전 조정

npm install react@18.2.0 react-dom@18.2.0
npm install

3. 실행

 npm run dev


2. Next.js의 페이지 라우팅 (관리자 페이지 추가 및 제거)


Next.js의 App Router는 아래 우선순위 규칙에 따라 라우팅을 결정합니다:

1️⃣ app/page.tsx가 있으면 → / 경로는 무조건 이걸 사용

2️⃣ app/page.tsx가 없고,

  • 가장 **루트에 가까운 page.tsx**가
  • 괄호 폴더((admin) 등) 아래에 있다면
    ➡ 해당 파일이 자동으로 루트(/) 경로의 페이지 역할을 하게 됩니다.

    *참고로 괄호로 된 파일 명은 라우팅 경로에 포함되지 않는다. 위의 예제에서는 src/app/admin 경로에 있는 page.tsx가 루트에 가장 가까운 page.tsx이기 때문에 제일 처음 랜더링 되게 된다.
src/
└── app/
    ├── (admin)/
    │   └── page.tsx     ✅ 라우팅 그룹이지만, 루트에 가장 가까운 page.tsx
    └── layout.tsx       ← 전체 앱에 공통 적용

다음과 같이 next.js에서는 기본적인 라우팅이 되기 때문에 기존 관리자 페이지의 페이지를 추가하거나 제거하려면 위의 규칙에 맞추어서 경로만 설정해 준다면 알아서 라우팅이 되게 됩니다.

Layout.tsx를 사용한 공통 페이지 레이아웃 설정

🧱 layout.tsx란?

Next.js App Router에서 페이지 그룹에 공통으로 적용되는 UI 구조를 설정하는 파일입니다.


✅ 기본 구조 예시

src/app/
├── layout.tsx         ← 전체 앱의 글로벌 레이아웃
├── page.tsx
├── (admin)/
│   ├── layout.tsx     ← 관리자 전용 레이아웃
│   └── dashboard/
│       └── page.tsx   ← 관리자 대시보드 페이지
└── login/
    └── page.tsx       ← 로그인 전용
  • 이 layout.tsx는 해당 디렉터리 아래 모든 page.tsx에 공통 적용됩니다.

🎯 사용 목적

목적layout.tsx에서 처리
🌍 공통 레이아웃 적용 Header, Footer, Sidebar
🎨 테마 적용 다크모드, Tailwind 클래스 래핑 등
🔒 인증 보호 로그인 여부 확인 후 children 제한
⚙️ 상태 제공 ThemeProvider, QueryClientProvider, RecoilRoot 등

🧠 특징 요약

특징설명
계층적으로 중첩 가능 app/layout.tsx, (admin)/layout.tsx, (auth)/layout.tsx 등
서버 컴포넌트 기본적으로 Server Component로 동작 ("use client" 안 붙임)
children 필수 내부에 렌더링될 page.tsx가 children으로 전달됨
라우팅 그룹에도 적용 가능 괄호 폴더((admin) 등) 안에도 layout.tsx 둘 수 있음

여기서 다루는 관리자 페이지 또한 기본 layout에 맞추어서 모든 경로에 sidebar와 footer, header등을 두었으며
이에 따라 각 컴포넌트를 변경하면 모든 페이지의 요소들이 변경되게 된다.

3. HOOK에 대한 개념

✅ Hook이란?

Hook은 함수형 컴포넌트에서 React의 기능들을 “붙여서” 사용할 수 있게 하는 함수입니다.

📌 대표적인 예:

Hook 이름설명
useState 컴포넌트 내 상태값 관리
useEffect 컴포넌트 생명주기 관리 (side effect)
useContext Context API 접근
useRef DOM 참조 또는 값 기억
useCallback 함수 메모이제이션
useMemo 값 메모이제이션
✅ 커스텀 Hook 여러분이 직접 만든 재사용 가능한 로직

✅ Hook은 어디에 써야 할까?

✅ 정답: 함수형 컴포넌트 내부에서만 사용해야 합니다.

// ✅ 올바른 예
export default function MyComponent() {
  const [count, setCount] = useState(0); // Hook 사용

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

이처럼 hook을 만들어서 사용한다면 함수형 컴포넌트의 최상단에서 이 hook을 가져오고 사용을 하면 됩니다.
각 요소인 컴포넌트에서 특정 함수를 실행시키고 싶다면 특정 함수를 hook으로 만들고 가져와서 사용하면 됩니다.

자체적으로 만든 커스텀 Hook의 사용예시는 다음과 같습니다.

import useSomething from '@/hooks/useSomething';

export default function SomeComponent() {
  const { value, setValue } = useSomething();
  return <div>{value}</div>;
}

✅ Hook을 컴포넌트에 "넣어서 쓰는 것"이 맞을까?

네, 맞습니다. Hook은 "컴포넌트 안에서 호출해서 사용하는 것"이 올바른 방식입니다.

💡 Hook = "이 컴포넌트가 작동할 때, 함께 작동해야 하는 기능 묶음"
→ 그래서 컴포넌트가 mount/unmount 될 때도 같이 관리됩니다.

 

응용 : 관리자 페이지에 블록체인 지갑을 붙이고 서명하는 hook만들기

먼저 최상단 페이지의 layout.tsx에 rainbow wallet을 추가시켜줍니다.

추가시키는 방법은 rainbow wallet docs에서 확인한 후 사용할 수 있습니다.
https://www.rainbowkit.com/docs/installation

 

Installation — RainbowKit

Get up and running with RainbowKit

www.rainbowkit.com


먼저 npm install을 해줍니다.

npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query


이후 import 부분을 진행합니다.

...
import { getDefaultConfig } from '@rainbow-me/rainbowkit';
const config = getDefaultConfig({
  appName: 'My RainbowKit App',
  projectId: 'YOUR_PROJECT_ID',
  chains: [mainnet, polygon, optimism, arbitrum, base],
  ssr: true, // If your dApp uses server side rendering (SSR)
});

config가 설정되어 있다면 다음과 같이 rianbow wallet을 먼저 주입시켜 준 후에 app을 실행시킵니다.

const queryClient = new QueryClient();
const App = () => {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>
          {/* Your App */}
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
};

만약 위의 템플릿 안에 해당 내용을 넣는다면 다음과 같이 넣을 수 있습니다.
적용하고자 하는 최상단 경로의 layout.tsx에 rainbow wallet의 설정 값을 넣어줍니다.

"use client";
import '@rainbow-me/rainbowkit/styles.css';  // ✅ RainbowKit 스타일 추가
import { Outfit } from "next/font/google";
import "./globals.css";
import AdminContent from "@/components/auth/AdminContent";
import WagmiWrapper from "@/components/web3/WagmiWrapper";
import { WagmiProvider, createConfig, http } from 'wagmi';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { mainnet, arbitrum, arbitrumGoerli } from 'wagmi/chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const outfit = Outfit({
    variable: "--font-outfit-sans",
    subsets: ["latin"],
});

// ✅ QueryClient 인스턴스 생성
const queryClient = new QueryClient();

// ✅ Wagmi 설정 구성 (최신 방식)
const wagmiConfig = createConfig({
    chains: [mainnet, arbitrum, arbitrumGoerli],
    transports: {
        [mainnet.id]: http(),
        [arbitrum.id]: http(),
        [arbitrumGoerli.id]: http(),
    }
});

export default function RootLayout({
    children,
}: Readonly<{
    children: React.ReactNode;
}>) {
    return (
        <html lang="en">
        <body className={`${outfit.variable} dark:bg-gray-900`}>
        <QueryClientProvider client={queryClient}>
            <WagmiProvider config={wagmiConfig}>
                <RainbowKitProvider>
                    <WagmiWrapper>
                        <AdminContent>
                            {children}
                        </AdminContent>
                    </WagmiWrapper>
                </RainbowKitProvider>
            </WagmiProvider>
        </QueryClientProvider>
        </body>
        </html>
    );
}

이런식으로 만든다면 화면 안에 지갑이 들어간 형태로 만들 수 있습니다.

그럼 지갑을 이용해서 서명을 하는 기능을 넣으려면 어떻게 해야 할까요?
여기서 서명을 하는 함수를 커스텀 hook으로 만든 후에 각 컴포넌트에서 해당 hook을 가져와서 사용하면 됩니다.

다음은 useSignature.ts 라는 자체적으로 제작한 hook입니다.

import { useAccount, useSignMessage } from 'wagmi';

export default function useSignature() {
    const { address, isConnected } = useAccount();
    const { signMessageAsync } = useSignMessage();

    const signAndSendRequest = async () => {
        if (!address || !isConnected) {
            alert("지갑이 연결되지 않았습니다.");
            return;
        }

        const message = `로그인을 위한 서명 요청: ${new Date().toISOString()}`;
        console.log(message);
        try {
            const signature = await signMessageAsync({ message });

            console.log("✅ 서명 성공:", signature);
        } catch (error) {
            console.error("❌ 서명 요청 오류:", error);
        }
    };

    return {
        signAndSendRequest
    };
}

위의 설정대로 rainbow wallet에서는 wagmi를 내부적으로 사용하고 있습니다. 따라서 wagmi의 함수를 hook에서 사용할 수 있습니다.
다음과 같이 signAndRequest 훅을 만든다면 이 훅을 가져와서 각 컴포넌트에서 원할때마다 지갑의 서명을 진행할 수 있습니다.

"use client";
import React from "react";
import { useModal } from "../../hooks/useModal";
import { Modal } from "../ui/modal";
import Button from "../ui/button/Button";
import Input from "../form/input/InputField";
import Label from "../form/Label";
import useSignature from "@/hooks/useSignature"; // ✅ 훅 경로는 실제 위치에 맞게


export default function UserInfoCard() {
  const { isOpen, openModal, closeModal } = useModal();
  const { signAndSendRequest } = useSignature(); // ✅ 훅 호출해서 서명 함수 가져오기

  const handleSave = () => {
    // Handle save logic here
    signAndSendRequest();  // ✅ 버튼 누르면 서명 시도
    console.log("Saving changes...");
    closeModal();
  };
  return (

보면 다음과 같이 hook경로를 통해서 useSignature라는 커스텀 훅을 가져와서 UserInfoCard라는 템플릿에 존재하는 Modal안에서 실행시킨 것을 볼 수 있습니다. 이렇게 된다면 Edit을 하기 전에 지갑주소에 대한 서명을 진행하고 save가 진행되게 됩니다.


728x90
반응형
Comments