🖥️Frontend/Next

[Next] 로컬 서버 API 호출하기

뉴발자 2025. 4. 27. 20:28
728x90

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

서버 API 구현

프로젝트 구조 생성

app 폴더 내에 api 폴더를 생성하고 path로 사용할 폴더를 하나 생성한 다음 route.ts 파일을 생성한다.

 

프로젝트의 구조는 다음과 같다.

 

 

route.ts

1. 함수 생성 및 Method 지정

먼저 호출할 함수의 HTTP Method의 이름으로 함수를 생성해준다.

 

필자는 POST로 테스트를 진행하기 위해서 POST로 작성해줬다.

export async function POST() {}

 

만약 POST Method가 아닌 다른 Method로 호출하게 되면 405 에러가 발생하게 된다.

 

2. Request 객체 사용하기

API 호출 시 NextRequest 타입의 객체에 사용자가 보낸 json 데이터를 받아와서 사용할 수 있다.

 

사용자가 입력한 id와 password 값을 받아오기 위해서 매개변수에 NextRequest 타입의 객체를 생성해주면 된다.

 

받아온 객체는 json() 함수를 사용해 json 객체로 파싱한 후 변수 안에 넣어준다.

 

변수에 저장된 json의 key 값으로 value를 가져와서 사용할 수 있다.

import { NextRequest } from "next/server";

export async function POST( req: NextRequest ) {
  const body = await req.json();
  const { id, password } = body;

  if (id === "123" && password === "123") {
    ...
  } else {
    ...
  }
}

 

3. 사용자의 요청에 응답 주기

사용자가 요청한 API에 응답을 주기 위해서 NextResponse 객체를 리턴해준다.

 

사용자 응답을 json 객체를 생성한 후 아래의 코드처럼 리턴해주면 된다.

import { NextRequest, NextResponse } from "next/server";

interface LoginResponseProps {
  code: string;
  msg: string;
  name?: string;
}

export async function POST( req: NextRequest ) {
  const body = await req.json();
  const { id, password } = body;
  let json: LoginResponseProps = { code: "", msg: "", name: "" };

  if (id === "123" && password === "123") {
    json = { code: "00", msg: "정상", name: "신대범" };
  } else {
    json = { code: "23", msg: "아이디 또는 비밀번호가 다릅니다" };
  }

  return new NextResponse(JSON.stringify(json));

}

 

 

클라이언트 구현

다음으로 서버 API를 호출하기 위한 클라이언트를 구현한다.

 

fetch 함수를 사용해서 로그인 기능을 구현한 후 로그인 성공 시 home 화면으로 이동하는 함수를 아래와 같이 작성했다.

"use client";

import { useCallback, useRef, useState } from "react";
import { useRouter } from "next/navigation";

interface LoginProps {
  id: string;
  password: string;
}

const defaultLoginInfo: LoginProps = {
  id: "",
  password: "",
};

const Login = () => {
  const [ userInfo, setUserInfo ] = useState(defaultLoginInfo);
  const idRef = useRef<HTMLInputElement>(null);
  const passwordRef = useRef<HTMLInputElement>(null);
  const router = useRouter();

  const onClickLogin = useCallback(async () => {
    try {
      if (!userInfo.id) {
        idRef.current.focus();
        return alert("아이디를 입력해 주세요");
      }

      if (!userInfo.password) {
        passwordRef.current.focus();
        return alert("비밀번호를 입력해 주세요");
      }

      const res = await fetch(
        "/api/login", 
        { 
          method: "POST",
          body: JSON.stringify({ 
            id: userInfo.id, 
            password: userInfo.password, 
          }),
        }
      );

      if (res.status === 200) {
        const data = await res.json();

        if (data.code === "23") {
          return alert("아이디 또는 비밀번호가 다릅니다");
        }
        
        router.push("/home");
        return alert(`${data.name}님, 환영합니다 🎉`);
      }
    } catch (err) {
      console.error(err);
    }
  }, [userInfo]);

  return (
    <div>
      <input
        ref={idRef}
        type="text"
        value={userInfo.id}
        onChange={(e) => setUserInfo({ ...userInfo, id: e.target.value })}
        placeholder="아이디"
      />
      <input 
        ref={passwordRef}
        type="text"
        value={userInfo.password}
        onChange={(e) => setUserInfo({ ...userInfo, password: e.target.value })}
        placeholder="비밀번호"
      />
      <button onClick={onClickLogin}>로그인</button>
    </div>
  );
};

export default Login;

 

 

 

 

 

 

 

 

 

 

728x90