개인정보-컴플라이언스-웹애플리케이션(10) - (시스템) 프론트 코드

2025. 1. 28. 22:37프로젝트

728x90
반응형
import React from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { useRecoilValue, useRecoilState } from "recoil";
import { authState } from "../../state/authState";
import { formState } from "../../state/formState";

function SystemRegistration() {
  const auth = useRecoilValue(authState); // 로그인된 사용자 정보 가져오기
  const [formData, setFormData] = useRecoilState(formState); // 전역 상태 관리
  const navigate = useNavigate();

  // 폼 데이터 변경 핸들러
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevState) => ({
      ...prevState,
      [name]: value || "", // 기본값 설정
    }));
  };

  // 폼 제출 핸들러
  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      if (!auth.user || !auth.user.id) {
        alert("사용자 정보가 없습니다. 다시 로그인해주세요.");
        return;
      }

      console.log("🚀 [POST] 요청 데이터:", {
        ...formData,
        user_id: auth.user.id,
      });
      console.log("📋 [DEBUG] reason 값:", formData.reason);

      const response = await axios.post(
        "http://localhost:3000/systems",
        { ...formData, user_id: auth.user.id },
        {
          withCredentials: true,
        }
      );

      console.log("✅ [POST] 응답 데이터:", response.data);
      alert("시스템 등록이 완료되었습니다!");
      navigate("/dashboard"); // 등록 완료 후 대시보드로 이동
    } catch (error) {
      console.error(
        "❌ [POST] 에러 응답:",
        error.response?.data || error.message
      );
      alert(error.response?.data?.message || "시스템 등록 실패");
    }
  };

  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-100">
      <div className="bg-white p-6 rounded-lg shadow-md w-3/4 max-w-2xl">
        <h1 className="text-3xl font-bold mb-6">시스템 등록 확인</h1>
        <form onSubmit={handleSubmit} className="space-y-4">
          <div>
            <label className="block text-gray-700 font-medium">
              시스템 이름
            </label>
            <input
              type="text"
              name="name"
              value={formData.name || ""}
              onChange={handleChange}
              className="w-full p-2 border rounded"
              placeholder="시스템 이름을 입력하세요"
              required
            />
          </div>
          <div>
            <label className="block text-gray-700 font-medium">
              최소 문항 수
            </label>
            <input
              type="number"
              name="min_subjects"
              value={formData.min_subjects || ""}
              onChange={handleChange}
              className="w-full p-2 border rounded"
              placeholder="최소 문항 수를 입력하세요"
              required
            />
          </div>
          <div>
            <label className="block text-gray-700 font-medium">
              최대 문항 수
            </label>
            <input
              type="number"
              name="max_subjects"
              value={formData.max_subjects || ""}
              onChange={handleChange}
              className="w-full p-2 border rounded"
              placeholder="최대 문항 수를 입력하세요"
              required
            />
          </div>
          <div>
            <label className="block text-gray-700 font-medium">처리 목적</label>
            <input
              type="text"
              name="purpose"
              value={formData.purpose || ""}
              onChange={handleChange}
              className="w-full p-2 border rounded"
              placeholder="처리 목적을 입력하세요"
              required
            />
          </div>
          <div>
            <label className="block text-gray-700 font-medium">
              민감 정보 포함 여부
            </label>
            <select
              name="is_private"
              value={formData.is_private || "포함"}
              onChange={handleChange}
              className="w-full p-2 border rounded"
            >
              <option value="포함">포함</option>
              <option value="미포함">미포함</option>
            </select>
          </div>
          <div>
            <label className="block text-gray-700 font-medium">
              고유 식별 정보 포함 여부
            </label>
            <select
              name="is_unique"
              value={formData.is_unique || "미포함"}
              onChange={handleChange}
              className="w-full p-2 border rounded"
            >
              <option value="포함">포함</option>
              <option value="미포함">미포함</option>
            </select>
          </div>
          <div>
            <label className="block text-gray-700 font-medium">
              주민등록번호 포함 여부
            </label>
            <select
              name="is_resident"
              value={formData.is_resident || "포함"}
              onChange={handleChange}
              className="w-full p-2 border rounded"
            >
              <option value="포함">포함</option>
              <option value="미포함">미포함</option>
            </select>
          </div>
          <div>
            <label className="block text-gray-700 font-medium">수집 근거</label>
            <select
              name="reason"
              value={formData.reason || "동의"}
              onChange={handleChange}
              className="w-full p-2 border rounded"
            >
              <option value="동의">동의</option>
              <option value="법적 근거">법적 근거</option>
              <option value="기타">기타</option>
            </select>
          </div>
          <button
            type="submit"
            className="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
          >
            확인
          </button>
        </form>
      </div>
    </div>
  );
}

export default SystemRegistration;

 

이 코드는 Recoil을 사용하여 일반 사용자가 시스템 등록을 처리하는 페이지입니다.

사용자는 입력 폼을 통해 시스템 정보를 입력하고, 해당 데이터를 서버에 전송하여 등록 요청을 처리합니다.

아래는 코드의 각 부분을 상세히 분석한 내용입니다.

 

1. 전역 상태 및 기본 설정

const auth = useRecoilValue(authState); // 로그인된 사용자 정보
const [formData, setFormData] = useRecoilState(formState); // 폼 데이터 상태
const navigate = useNavigate(); // 페이지 이동을 위한 훅

 

authState: 로그인된 사용자 정보를 가져오는 Recoil 상태입니다.
formState: 입력된 폼 데이터를 전역에서 관리하기 위한 Recoil 상태입니다.
navigate: 사용자가 등록을 완료하거나 오류가 발생했을 때 페이지를 이동시키는 함수입니다.

 

2. 폼 데이터 관리

const handleChange = (e) => {
  const { name, value } = e.target;
  setFormData((prevState) => ({
    ...prevState,
    [name]: value || "", // 기본값 설정
  }));
};


사용자가 입력한 값(value)을 해당 입력 필드의 이름(name)과 매핑하여 formData 상태에 저장합니다.
입력값이 비어 있을 경우 기본값으로 빈 문자열을 저장합니다.

 

폼 제출 핸들러

const handleSubmit = async (e) => {
  e.preventDefault(); // 폼 제출 시 페이지 리로드 방지

  try {
    if (!auth.user || !auth.user.id) {
      alert("사용자 정보가 없습니다. 다시 로그인해주세요.");
      return;
    }

    console.log("🚀 [POST] 요청 데이터:", {
      ...formData,
      user_id: auth.user.id,
    });
    console.log("📋 [DEBUG] reason 값:", formData.reason);

    const response = await axios.post(
      "http://localhost:3000/systems",
      { ...formData, user_id: auth.user.id }, // 사용자 ID 포함
      { withCredentials: true } // 쿠키 인증 포함
    );

    console.log("✅ [POST] 응답 데이터:", response.data);
    alert("시스템 등록이 완료되었습니다!");
    navigate("/dashboard"); // 성공 시 대시보드로 이동
  } catch (error) {
    console.error(
      "❌ [POST] 에러 응답:",
      error.response?.data || error.message
    );
    alert(error.response?.data?.message || "시스템 등록 실패");
  }
};

 

1. 입력값 검증:
사용자 정보(auth.user.id)가 없으면 경고 메시지를 출력하고 종료.
2. API 요청:
서버에 formData와 사용자 ID를 포함한 데이터를 전송.
3. withCredentials: true:
서버로 인증 쿠키를 함께 전송하여 사용자를 인증.
4. 요청 성공:
성공 메시지를 출력하고 /dashboard 페이지로 이동.
요청 실패:
오류 메시지를 출력하며, 서버에서 제공한 메시지가 없으면 기본 메시지를 사용.