Repository: 9git9git/9git-frontend
Pull request #20: ✨ [SCRUM-112] FE 개인정보 동의서 컴포넌트 제작
Author: login: Kristyn-Yoon
Created: 2025-04-07T21:21:22Z | Merged: 2025-04-08T05:18:30Z

## Explain this Pull Request 🙏

- Jira Ticket : SCRUM-112
- Figma URL: https://www.figma.com/design/3jOOTGVA7NqLIWRyvKJuR5/Proto?node-id=0-1&t=oLiOrgMJuNzkxbxG-1

## What has changed? 🤔
- 개인 정보 동의서 컴포넌트 구현
- 체크박스 누를시 버스 활성화 구현

## Whether needed review 🖍

<!-- 마크다운의 빈 체크박스 문법은 [ ] / 채워진 체크박스 문법은 [x] 입니다. -->

- [x] 🙋🏻 리뷰가 필요합니다!
- [ ] 🙅🏻 리뷰가 필요하지 않습니다!


## Document🗒️ (Optional)

## Screenshot 📸 (Optional)
![image](https://github.com/user-attachments/assets/aad6fd1c-ff06-4653-a5b9-09219880c32f)
![image](https://github.com/user-attachments/assets/666a95e0-728c-4fce-a394-e35a03ba0113)



## Test Checklist ✅ (Optional)

<!-- 마크다운의 빈 체크박스 문법은 [ ] / 채워진 체크박스 문법은 [x] 입니다. -->



Diff:
diff --git a/components/service/profile/ProfileHeaderContent.tsx b/components/service/profile/ProfileHeaderContent.tsx
index 0f2957f..79fc6e0 100644
--- a/components/service/profile/ProfileHeaderContent.tsx
+++ b/components/service/profile/ProfileHeaderContent.tsx
@@ -3,7 +3,7 @@
 import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
 import { Button } from '@/components/ui/button';
 import { useModalStore } from '@/stores/modal';
-import ProfileModal from './ProfileModal/ProfileModal';
+import ProfileModal from '@/components/service/profile/ProfileModal/ProfileModal';
 
 export const ProfileHeaderContent = () => {
   const { openModal } = useModalStore();
diff --git a/components/service/profile/ProfileModal/ConsentConfirm.tsx b/components/service/profile/ProfileModal/ConsentConfirm.tsx
new file mode 100644
index 0000000..b7e0bf4
--- /dev/null
+++ b/components/service/profile/ProfileModal/ConsentConfirm.tsx
@@ -0,0 +1,98 @@
+'use client';
+
+import { useForm } from 'react-hook-form';
+import { Checkbox } from '@/components/ui/checkbox';
+import { PencilLine } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import {
+  Form,
+  FormField,
+  FormItem,
+  FormLabel,
+  FormControl,
+  FormMessage,
+} from '@/components/ui/form';
+
+interface FormValues {
+  isConsent: boolean; // 체크박스 여부
+}
+
+interface Props {
+  onConfirm: () => void; // 완료 버튼을 눌렀을 때 실행될 콜백 함수
+}
+
+export default function ConsentConfirm({ onConfirm }: Props) {
+  // 입력값 상태를 관리하는 훅
+  const form = useForm<FormValues>({
+    defaultValues: {
+      isConsent: false, // 기본값으로 consent는 false (체크 안 된 상태)
+    },
+  });
+
+  // 체크박스 상태를 실시간으로 감시
+  const consentChecked = form.watch('isConsent');
+
+  // 폼 제출 시 실행되는 함수
+  const handleSubmit = (data: FormValues) => {
+    if (data.isConsent) {
+      // 체크되었으면 onConfirm 콜백 실행
+      onConfirm();
+    } else {
+      // 체크되지 않았을 경우 사용자에게 알림
+      alert('동의를 체크해주세요.');
+    }
+  };
+
+  return (
+    // 전체 폼을 감싸는 Form 컴포넌트 (shadcn + react-hook-form 연결)
+    <Form {...form}>
+      {/* 실제 form 태그 - handleSubmit으로 제출 시 처리 */}
+      <form onSubmit={form.handleSubmit(handleSubmit)} className="flex flex-col space-y-6">
+        {/* FormField: consent 필드에 대한 입력 요소 정의. 각각의 필드를 정의하고 상태와 연결 */}
+        <FormField
+          control={form.control}
+          name="isConsent"
+          render={({ field }) => (
+            // FormItem은 하나의 필드 묶음을 감싸는 역할 (label + control + message)
+            <FormItem className="flex items-center space-x-2">
+              {/* FormControl: 실제 입력 요소인 체크박스를 감싸는 부분 */}
+              <FormControl>
+                <Checkbox
+                  id="privacy-check"
+                  checked={field.value}
+                  // 'indeterminate' 상태는 false로 처리 (선택 안 한 상태)
+                  onCheckedChange={(value) => {
+                    field.onChange(value === 'indeterminate' ? false : value);
+                  }}
+                />
+              </FormControl>
+
+              {/* FormLabel: 체크박스 옆 텍스트 */}
+              <FormLabel htmlFor="privacy-check" className="text-sm font-base text-secondary">
+                개인정보 수집 및 이용에 동의합니다 (필수)
+              </FormLabel>
+
+              {/* FormMessage: 에러 메시지를 표시할 공간 (유효성 검증 시 사용됨) */}
+              <FormMessage />
+            </FormItem>
+          )}
+        />
+
+        {/* 완료 버튼 (Button 컴포넌트 사용) */}
+        <Button
+          type="submit"
+          variant="ghost" // 투명한 스타일
+          className="bg-transparent hover:bg-transparent shadow-none border-none px-0 py-0 h-auto"
+        >
+          <span
+            className={`${
+              consentChecked ? 'text-secondary icon-color' : 'text-beige-deco icon-color'
+            } flex items-center gap-1`}
+          >
+            <PencilLine size={16} /> 완료
+          </span>
+        </Button>
+      </form>
+    </Form>
+  );
+}
diff --git a/components/service/profile/ProfileModal/ProfileModal.tsx b/components/service/profile/ProfileModal/ProfileModal.tsx
index c92e640..9e97e20 100644
--- a/components/service/profile/ProfileModal/ProfileModal.tsx
+++ b/components/service/profile/ProfileModal/ProfileModal.tsx
@@ -1,19 +1,22 @@
 'use client';
 
 import { useState } from 'react';
-import { Checkbox } from '@/components/ui/checkbox';
 import JobSelectbar from '@/components/service/profile/ProfileModal/JobSelectbar';
+import { Separator } from '@/components/ui/separator';
+import { TextScroll } from '@/components/service/profile/ProfileModal/TextScroll';
+import { PERSONAL_INFORMATION_AGREEMENT } from '@/constants/PERSONAL_INFORMATION_AGREEMENT';
+import ConsentConfirm from '@/components/service/profile/ProfileModal/ConsentConfirm';
 
 export default function ProfileModal() {
   const [checked, setChecked] = useState(false);
   const [selectedJob, setSelectedJob] = useState<Job | ''>('');
-
-  const handleCheckedChange = (value: boolean | 'indeterminate') => {
-    setChecked(value === true);
+  const handleSubmit = () => {
+    console.log('제출 완료!'); // 완료 버튼 클릭 시 처리할 작업
   };
 
   return (
-    <div className="p-4 space-y-4">
+    <div className="p-4 space-y-3">
+
       {/* 헤더 */}
       <h2 className="text-lg font-semibold">나의 정보</h2>
       {/* 아이디 */}
@@ -25,8 +28,12 @@ export default function ProfileModal() {
       {/* 직업 */}
       <JobSelectbar selectedJob={selectedJob} setSelectedJob={setSelectedJob} />
 
-      {/* 체크박스 */}
-      <Checkbox checked={checked} onCheckedChange={handleCheckedChange} />
+      {/* 개인정보 동의서 */}
+      <Separator className="bg-beige-deco" />
+      <TextScroll text={PERSONAL_INFORMATION_AGREEMENT} />
+
+      {/* 체크박스 + 완료 버튼 */}
+      <ConsentConfirm onConfirm={handleSubmit} />
     </div>
   );
 }
diff --git a/components/service/profile/ProfileModal/TextScroll.tsx b/components/service/profile/ProfileModal/TextScroll.tsx
new file mode 100644
index 0000000..4cc5686
--- /dev/null
+++ b/components/service/profile/ProfileModal/TextScroll.tsx
@@ -0,0 +1,15 @@
+'use client';
+
+import React from 'react';
+
+type Props = {
+  text: string;
+};
+
+export const TextScroll = ({ text }: Props) => {
+  return (
+    <div className="w-full h-20 overflow-y-scroll bg-white border border-beige-deco px-3 py-2 text-xs whitespace-pre-wrap">
+      {text}
+    </div>
+  );
+};
diff --git a/components/ui/checkbox.tsx b/components/ui/checkbox.tsx
index f0cb3b9..f9730d5 100644
--- a/components/ui/checkbox.tsx
+++ b/components/ui/checkbox.tsx
@@ -13,7 +13,7 @@ function Checkbox({ className, ...props }: CheckboxProps) {
     <CheckboxPrimitive.Root
       data-slot="checkbox"
       className={cn(
-        'w-[18px] h-[18px] border-[2px] border-secondary rounded-[4px] data-[state=checked]:bg-primary data-[state=checked]:border-primary',
+        'w-[18px] h-[18px] border-[2px] border-secondary rounded-[4px] data-[state=checked]:bg-primary data-[state=checked]:border-primary cursor-pointer',
         className
       )}
       {...props}
diff --git a/constants/PERSONAL_INFORMATION_AGREEMENT.ts b/constants/PERSONAL_INFORMATION_AGREEMENT.ts
new file mode 100644
index 0000000..ba7aaf9
--- /dev/null
+++ b/constants/PERSONAL_INFORMATION_AGREEMENT.ts
@@ -0,0 +1,9 @@
+export const PERSONAL_INFORMATION_AGREEMENT = `나의 정보 수집 및 이용 동의 (필수)
+
+1. 수집 목적: 맞춤형 서비스 제공, 서비스 품질 향상, 통계 분석
+2. 수집 항목: 성별, 나이, 직업
+3. 보유 및 이용 기간: 회원 탈퇴 시까지 (단, 관계 법령에 따라 보존할 필요가 있는 경우 해당 기간 동안 보관)
+4. 동의 거부 권리 및 거부 시 불이익: 필수 정보 수집에 동의하지 않을 경우 서비스 이용이 제한됩니다.
+
+위 개인정보 수집 및 이용에 대한 동의를 거부할 권리가 있습니다. 그러나 동의를 거부할 경우 서비스 이용이 제한됩니다.
+`;


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @Kristyn-Yoon, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request introduces a new personal information consent component for the frontend, as part of the SCRUM-112 ticket. The changes include the implementation of the consent form UI, a text scroll component to display the agreement details, and a checkbox to handle user consent. The modal's width has also been adjusted. The component includes logic to enable a submit button upon checking the consent checkbox.

### Highlights
* **New Component: ConsentConfirm**: Introduces a new component `ConsentConfirm.tsx` which includes a checkbox for the user to agree to the privacy policy and an action button that is enabled when the checkbox is checked.
* **New Component: TextScroll**: Introduces a new component `TextScroll.tsx` which displays the personal information agreement in a scrollable text area.
* **New Component: ProfileModal**: Creates a new `ProfileModal.tsx` component that integrates the `TextScroll` and `ConsentConfirm` components to display the personal information agreement and handle user consent.
* **Modal Container Width Adjustment**: The width of the `ModalContainer` component has been increased from `400px` to `440px` to better accommodate the new content.
* **Checkbox Styling**: The checkbox component now has `cursor-pointer` to indicate it is clickable.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **components/service/modals/ProfileModal.tsx**
  * This file was removed, and its functionality was replaced by the new ProfileModal component in the profile directory.
* **components/service/profile/ProfileHeaderContent.tsx**
  * Updated import path for `ProfileModal` to reflect its new location.
  * Minor formatting change: Removed unnecessary line break in the logout button.
* **components/service/profile/ProfileModal/ConsentConfirm.tsx**
  * Created a new component `ConsentConfirm` to handle the consent checkbox and confirmation button logic.
  * Includes state management for the checkbox and a callback function for handling the confirmation action.
* **components/service/profile/ProfileModal/ProfileModal.tsx**
  * Created a new `ProfileModal` component to display user information and the personal information agreement.
  * Integrates the `TextScroll` and `ConsentConfirm` components.
  * Includes a placeholder `handleSubmit` function for handling the submission of the consent form.
* **components/service/profile/ProfileModal/TextScroll.tsx**
  * Created a new `TextScroll` component to display scrollable text, used for the personal information agreement.
  * Accepts a `text` prop to render the content.
* **components/shared/Modal/ModalContainer.tsx**
  * Increased the `max-w` (max width) of the modal container from `400px` to `440px`.
* **components/ui/checkbox.tsx**
  * Added `cursor-pointer` class to the checkbox for better UX.
* **constants/PERSONAL_INFORMATION_AGREEMENT.ts**
  * Created a new constant to store the personal information agreement text.

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

> A checkbox appears,
> A click, a choice to make,
> Consent now given.


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-07T21:21:32Z
user: login: gemini-code-assist[bot]
body: ## Code Review
The pull request introduces a personal information consent component, which is a great addition for user privacy. The implementation seems well-structured, but there are a few points that could be improved for better maintainability and user experience.

### Summary of Findings
* **Accessibility Considerations**: Ensure that all interactive elements, including checkboxes and buttons, are accessible to users with disabilities. This includes providing proper ARIA attributes and keyboard navigation support.
* **Error Handling**: The `handleSubmit` function in `ProfileModal.tsx` currently only logs a message to the console. Implement proper error handling and user feedback mechanisms for cases where the submission fails.
* **State Management**: Consider using a more robust state management solution (e.g., Zustand, Redux) if the application grows in complexity. This can help manage the state of the modal and its components more effectively.

### Merge Readiness
The code introduces a valuable feature, but there are some improvements that should be addressed before merging. Specifically, focus on enhancing accessibility, implementing proper error handling, and considering a more robust state management solution for future scalability. I am unable to approve this pull request, and recommend that others review and approve this code before merging.
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-04-07T21:21:46Z
user: login: trivialcoding6
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T23:12:59Z
user: login: trivialcoding6
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T02:40:30Z
user: login: trivialcoding6
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-08T05:03:41Z
user: login: Yoonsoyoung02
body: 확인했습니다~ 수고 하셨습니다 :) /lgtm
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-08T05:16:34Z

Review comments:
diff_hunk: @@ -0,0 +1,35 @@
+'use client';
+
+import { useState } from 'react';
+import { Separator } from '@/components/ui/separator';
+import { TextScroll } from '@/components/service/profile/ProfileModal/TextScroll';
+import { PERSONAL_INFORMATION_AGREEMENT } from '@/constants/PERSONAL_INFORMATION_AGREEMENT';
+import ConsentConfirm from '@/components/service/profile/ProfileModal/ConsentConfirm';
+
+export default function ProfileModal() {
+  const [checked, setChecked] = useState(false);
+  const handleSubmit = () => {
+    console.log('제출 완료!'); // 완료 버튼 클릭 시 처리할 작업
+  };
path: components/service/profile/ProfileModal/ProfileModal.tsx
user: login: gemini-code-assist[bot]
body: ![high](https://img.shields.io/badge/priority-high-orange)

The `handleSubmit` function currently only logs to the console. Implement actual submission logic and error handling. Consider providing user feedback (e.g., a success message or an error message) after the submission attempt.

```typescript
 const handleSubmit = async () => {
    try {
      // TODO: Implement actual submission logic here
      console.log('제출 완료!');
      // Optionally, show a success message to the user
    } catch (error) {
      console.error('Submission failed:', error);
      // TODO: Display an error message to the user
    }
  };
```
created_at: 2025-04-07T21:21:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+import { useState } from 'react';
+import { Checkbox } from '@/components/ui/checkbox';
+import { PencilLine } from 'lucide-react';
+import { ActionButton } from '@/components/common/ActionButton';
+
+interface Props {
+  onConfirm: () => void;
+}
+
+export default function ConsentConfirm({ onConfirm }: Props) {
+  const [checked, setChecked] = useState(false);
+
+  const handleCheck = (value: boolean | 'indeterminate') => {
+    setChecked(value === 'indeterminate' ? false : value);
+  };
+
+  return (
+    <div className="flex flex-col space-y-6">
+      <div className="flex items-center space-x-2">
+        <Checkbox checked={checked} onCheckedChange={handleCheck} id="privacy-check" />
path: components/service/profile/ProfileModal/ConsentConfirm.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider adding an `aria-label` to the Checkbox component for improved accessibility. This will help screen reader users understand the purpose of the checkbox.

```typescript
<Checkbox checked={checked} onCheckedChange={handleCheck} id="privacy-check" aria-label="개인정보 수집 및 이용 동의" />
```
created_at: 2025-04-07T21:21:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+import { useState } from 'react';
+import { Checkbox } from '@/components/ui/checkbox';
+import { PencilLine } from 'lucide-react';
+import { ActionButton } from '@/components/common/ActionButton';
+
+interface Props {
+  onConfirm: () => void;
+}
+
+export default function ConsentConfirm({ onConfirm }: Props) {
+  const [checked, setChecked] = useState(false);
+
+  const handleCheck = (value: boolean | 'indeterminate') => {
+    setChecked(value === 'indeterminate' ? false : value);
+  };
+
+  return (
+    <div className="flex flex-col space-y-6">
+      <div className="flex items-center space-x-2">
+        <Checkbox checked={checked} onCheckedChange={handleCheck} id="privacy-check" />
+        <label htmlFor="privacy-check" className="text-sm font-base text-secondary">
+          개인정보 수집 및 이용에 동의합니다 (필수)
+        </label>
+      </div>
+
+      <ActionButton onClick={onConfirm}>
+        <span
+          className={`${
+            checked ? 'text-secondary icon-color' : 'text-beige-deco icon-color'
+          } flex items-center gap-1`}
+        >
+          <PencilLine size={16} /> 완료
+        </span>
+      </ActionButton>
path: components/service/profile/ProfileModal/ConsentConfirm.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `ActionButton` component's styling is dependent on the `checked` state. It would be better to disable the button when the checkbox is unchecked to prevent submission. This provides a clearer visual cue to the user.

```typescript
<ActionButton onClick={onConfirm} disabled={!checked}>
        <span
          className={`${
            checked ? 'text-secondary icon-color' : 'text-beige-deco icon-color'}
           flex items-center gap-1`}
        >
          <PencilLine size={16} /> 완료
        </span>
      </ActionButton>
```
created_at: 2025-04-07T21:21:46Z
author_association: NONE
diff_hunk: @@ -13,7 +13,7 @@ export const ModalContainer = ({ children }: Props) => {
     // 모달 배경
     // 모달 노출 시 배경 흐리게
     <div
-      className="fixed inset-0 z-50 max-w-[400px] mx-auto flex items-center justify-center bg-black/70"
+      className="fixed inset-0 z-50 max-w-[440px] mx-auto flex items-center justify-center bg-black/70"
path: components/shared/Modal/ModalContainer.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The modal's `max-width` has been increased from `400px` to `440px`. Ensure that this change doesn't negatively impact the layout on smaller screens. Consider using a responsive approach (e.g., media queries) to adjust the modal's width based on the screen size.

```typescript
className="fixed inset-0 z-50 max-w-[90%] md:max-w-[440px] mx-auto flex items-center justify-center bg-black/70"
```
created_at: 2025-04-07T21:21:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,37 @@
+import { useState } from 'react';
+import { Checkbox } from '@/components/ui/checkbox';
+import { PencilLine } from 'lucide-react';
+import { ActionButton } from '@/components/common/ActionButton';
+
+interface Props {
+  onConfirm: () => void;
+}
+
+export default function ConsentConfirm({ onConfirm }: Props) {
+  const [checked, setChecked] = useState(false);
+
+  const handleCheck = (value: boolean | 'indeterminate') => {
+    setChecked(value === 'indeterminate' ? false : value);
+  };
+
+  return (
+    <div className="flex flex-col space-y-6">
+      <div className="flex items-center space-x-2">
+        <Checkbox checked={checked} onCheckedChange={handleCheck} id="privacy-check" />
path: components/service/profile/ProfileModal/ConsentConfirm.tsx
user: login: trivialcoding6
body: @Kristyn-Yoon 
정민님도 이제 form 관련된 컴포넌트들은 Form + react-hook-form 조합으로 사용하는걸 권장드립니닷!

```tsx
"use client"

import Link from "next/link"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"

import { toast } from "@/components/hooks/use-toast"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
} from "@/components/ui/form"

const FormSchema = z.object({
  mobile: z.boolean().default(false).optional(),
})

export function CheckboxReactHookFormSingle() {
  const form = useForm<z.infer<typeof FormSchema>>({
    resolver: zodResolver(FormSchema),
    defaultValues: {
      mobile: true,
    },
  })

  function onSubmit(data: z.infer<typeof FormSchema>) {
    toast({
      title: "You submitted the following values:",
      description: (
        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
          <code className="text-white">{JSON.stringify(data, null, 2)}</code>
        </pre>
      ),
    })
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
        <FormField
          control={form.control}
          name="mobile"
          render={({ field }) => (
            <FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4 shadow">
              <FormControl>
                <Checkbox
                  checked={field.value}
                  onCheckedChange={field.onChange}
                />
              </FormControl>
              <div className="space-y-1 leading-none">
                <FormLabel>
                  Use different settings for my mobile devices
                </FormLabel>
                <FormDescription>
                  You can manage your mobile notifications in the{" "}
                  <Link href="/examples/forms">mobile settings</Link> page.
                </FormDescription>
              </div>
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  )
}

```
created_at: 2025-04-07T23:12:46Z
author_association: COLLABORATOR
diff_hunk: @@ -1,37 +1,98 @@
-import { useState } from 'react';
+'use client';
+
+import { useForm } from 'react-hook-form';
 import { Checkbox } from '@/components/ui/checkbox';
 import { PencilLine } from 'lucide-react';
-import { ActionButton } from '@/components/common/ActionButton';
+import { Button } from '@/components/ui/button';
+import {
+  Form,
+  FormField,
+  FormItem,
+  FormLabel,
+  FormControl,
+  FormMessage,
+} from '@/components/ui/form';
+
+interface FormValues {
+  consent: boolean; // 체크박스 여부
+}
 
 interface Props {
-  onConfirm: () => void;
+  onConfirm: () => void; // 완료 버튼을 눌렀을 때 실행될 콜백 함수
 }
 
 export default function ConsentConfirm({ onConfirm }: Props) {
-  const [checked, setChecked] = useState(false);
+  // 입력값 상태를 관리하는 훅
+  const form = useForm<FormValues>({
path: components/service/profile/ProfileModal/ConsentConfirm.tsx
user: login: trivialcoding6
body: @Kristyn-Yoon 
아마 ProfileModal 의 전체 form 상태를 관리해야할거라 여기 부모 컴포넌트인 <ProfileModal /> 에서 관리하시는게 나을거에요!

https://www.react-hook-form.com/api/useformcontext/ 
위 링크 참고해보시면 됩니닷 :)
created_at: 2025-04-08T02:39:26Z
author_association: COLLABORATOR
diff_hunk: @@ -1,37 +1,98 @@
-import { useState } from 'react';
+'use client';
+
+import { useForm } from 'react-hook-form';
 import { Checkbox } from '@/components/ui/checkbox';
 import { PencilLine } from 'lucide-react';
-import { ActionButton } from '@/components/common/ActionButton';
+import { Button } from '@/components/ui/button';
+import {
+  Form,
+  FormField,
+  FormItem,
+  FormLabel,
+  FormControl,
+  FormMessage,
+} from '@/components/ui/form';
+
+interface FormValues {
+  consent: boolean; // 체크박스 여부
+}
 
 interface Props {
-  onConfirm: () => void;
+  onConfirm: () => void; // 완료 버튼을 눌렀을 때 실행될 콜백 함수
 }
 
 export default function ConsentConfirm({ onConfirm }: Props) {
-  const [checked, setChecked] = useState(false);
+  // 입력값 상태를 관리하는 훅
+  const form = useForm<FormValues>({
+    defaultValues: {
+      consent: false, // 기본값으로 consent는 false (체크 안 된 상태)
path: components/service/profile/ProfileModal/ConsentConfirm.tsx
user: login: trivialcoding6
body: @Kristyn-Yoon 
보통 boolean 값이면 앞에 is를 붙여서 isConsent 라고 하면 좋을 거 같습니다.
created_at: 2025-04-08T02:40:11Z
author_association: COLLABORATOR

PR comments:
user: login: gemini-code-assist[bot]
created_at: 2025-04-08T05:02:54Z
body: > [!WARNING]
> You have reached your daily quota limit. As a reminder, free tier users are limited to 5 requests per day. Please wait up to 24 hours and I will start processing your requests again!
author_association: NONE
user: login: gemini-code-assist[bot]
created_at: 2025-04-08T05:03:04Z
body: > [!WARNING]
> You have reached your daily quota limit. As a reminder, free tier users are limited to 5 requests per day. Please wait up to 24 hours and I will start processing your requests again!
author_association: NONE
user: login: gemini-code-assist[bot]
created_at: 2025-04-08T05:03:19Z
body: > [!WARNING]
> You have reached your daily quota limit. As a reminder, free tier users are limited to 5 requests per day. Please wait up to 24 hours and I will start processing your requests again!
author_association: NONE
user: login: trivialcoding6
created_at: 2025-04-08T05:03:31Z
body: LGTM 👍👍👍
author_association: COLLABORATOR