🖥️Frontend/Next

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

뉴발자 2025. 4. 27.
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

댓글