Repository: rak517/Epigram
Pull request #187: EP-120 헤더 프로필 영역 클릭 시 드롭다운 적용 
Author: login: jaehyeongjung
Labels: name: Priority: Medium :bookmark:
description: 우선순위 보통
name: Type: Refactor ♻️
description: 코드 리팩토링
name: Type: Test 🧪
description: test 관련(storybook, jest...)
Created: 2025-04-04T14:41:06Z | Merged: 2025-04-07T10:49:22Z

## ❓이슈
- close #183 

## :writing_hand: Description
기존 헤더는 마이 프로필 클릭 시 로그인이 되어있다면 마이프로필로 이동하게, 아니라면 로그인 페이지로 이동하여
본인의 현재 로그인 여부를 판단하기 쉽지 않았습니다.

## 렌딩 헤더 (로그인 여부를 판단) 
로그인이 안 되어 있는 상태에서 프로필 이미지를 클릭하면, 드롭다운으로 [로그인] 하나만 내려집니다.
<img width="617" alt="스크린샷 2025-04-04 오후 11 04 46" src="https://github.com/user-attachments/assets/190f69ee-6981-44cb-b188-aa4a73389a1f" />



-- 


로그인이 되어 있는 상태에서 프로필 이미지를 클릭하면, 드롭다운으로 [마이페이지]와 [로그아웃] 두 개가 내려집니다

<img width="617" alt="스크린샷 2025-04-04 오후 11 04 57" src="https://github.com/user-attachments/assets/a4f91196-3975-424d-b601-cf9ad5e22ccf" />



## 렌딩 헤더 (로그인 여부 판단 필요없음)
이미 로그인이 되어있는 상태 전제이기 때문에, 프로필을 클릭하면 드롭다운으로 [마이페이지]와 [로그아웃] 두 개가 내려집니다.

<img width="617" alt="스크린샷 2025-04-04 오후 11 05 05" src="https://github.com/user-attachments/assets/5ccf971e-7e70-48ff-99fa-803b7355a81e" />

## others 

- handleSelect는 공통적으로 쓰여서 hook으로 따로 빼서 가져왔습니다.
- 원래 처음은 로그인 여부를 체크하는 유틸을 만들려다가, getUser 만들어두신 함수를 이용해서 체크했습니다.
- 드롭다운의 코드를 약간 수정했습니다 trigger로 프로필이미지 받게

## 현재 문제 
- 바뀐 헤더에 따른 기존 E2E 코드 수정 해야하는데, feed 페이지와 epigrams 페이지에서는 수정완료 해서 테스트 성공
- landing 페이지에서는 landing 헤더를 이용하는데 getUser를 이용해서 로그인 여부를 판단하는 헤더이기 때문에 
- 기존에 lading E2E 코드는 test 값으로 로그인 처리를 했는데 실제 없는 유저라 E2E가 막히는 것으로 판단
- 그래서, test 아이디를 직접 기입하는 방식으로 로그인 처리 하니깐 테스트는 성공 
- 근데 파이어폭스에서만 통과를 못해서 이것만 몇 시간 잡고 있어서 롤백해서 다시 PR 올리고 작업을 해 보려고 합니다.
- 지금 PR에서 올린 landing E2E는 임시처리 해 두었고, 기존 코드입니다 


<!-- 어떤 내용의 PR인지 작성해주세요. (ex. 메인 페이지 레이아웃 작업) -->
<!-- ⚠️ PR에는 해당 PR의 제목에 해당하는 내용만 들어가 있어야 합니다!  -->

## :white_check_mark: Checklist

### PR


<!-- 작성중인 PR인 경우, Draft 모드로 생성해주세요. -->

- [x] Branch Convention 확인
  > `feat/*` 기능 구현, `fix/*` 버그 수정, `refactor/*` 개선
- [x] Base Branch 확인
- [x] 적절한 Label 지정
- [x] Assignee 및 Reviewer 지정

### Test

- [x] 로컬 작동 확인

### Additional Notes

<!-- 추가 사항이 있을 경우, Todo list를 작성해주세요. -->

- [x] feed페이지와 epigrams페이지의 관련 E2E는 수정을 마치고 모두 테스트 통과 받은 상황입니다
- [ ] lading 페이지 E2E 문제 해결 해야 합니다.



Linked issue #183: [EP-120] Refactor: Header 부분 드롭다운으로 로그아웃 추가
### 🎟️ 상위 작업 (에픽 키)

EP-11

### 📝 상세 내용 (Description)

Header 프로필 영역 클릭 시 드롭다운으로 마이페이지/ 로그아웃로 수정 

### ✅ 체크리스트 (Tasks)

- [ ] 프로필 영역 클릭 시 페이지 이동에서 ->  드롭다운으로 수정
- [x] 드롭다운 영역 1. 마이페이지 이동 2. 로그아웃 


### 📄 참고 자료

_No response_

Diff:
diff --git a/clicked-icon.png b/clicked-icon.png
new file mode 100644
index 00000000..bd4d57bb
Binary files /dev/null and b/clicked-icon.png differ
diff --git a/e2e/epigrams.spec.ts b/e2e/epigrams.spec.ts
index 72771fce..272395f2 100644
--- a/e2e/epigrams.spec.ts
+++ b/e2e/epigrams.spec.ts
@@ -54,7 +54,8 @@ test.describe('메인 페이지', () => {
   });
 
   test('헤더의 프로필 이미지를 클릭하면 마이페이지로 이동한다', async () => {
-    await page.getByRole('link', { name: '프로필 이미지' }).click();
+    await page.getByRole('button', { name: '프로필 이미지' }).click();
+    await page.getByText('마이페이지').click();
     await expect(page).toHaveURL('http://localhost:3000/mypage');
   });
 
diff --git a/e2e/feed.spec.ts b/e2e/feed.spec.ts
index 5fac9e37..bc5a4931 100644
--- a/e2e/feed.spec.ts
+++ b/e2e/feed.spec.ts
@@ -48,7 +48,8 @@ test.describe('로그인 이후 피드 페이지', () => {
   });
 
   test('헤더의 프로필 이미지를 클릭하면 마이페이지로 이동한다', async () => {
-    await page.getByRole('link', { name: '프로필 이미지' }).click();
+    await page.getByRole('button', { name: '프로필 이미지' }).click();
+    await page.getByText('마이페이지').click();
     await expect(page).toHaveURL('http://localhost:3000/mypage');
   });
 
diff --git a/e2e/landing.spec.ts b/e2e/landing.spec.ts
index e7115553..4ac492ed 100644
--- a/e2e/landing.spec.ts
+++ b/e2e/landing.spec.ts
@@ -8,6 +8,7 @@ test.describe('랜딩 페이지', () => {
     context = await browser.newContext();
     page = await context.newPage();
     await page.goto('/');
+    await page.waitForTimeout(1000);
   });
 
   test.afterEach(async () => {
@@ -29,7 +30,8 @@ test.describe('랜딩 페이지', () => {
   });
 
   test('로그인이 안되어 있으면, 유저 아이콘 클릭 시 로그인 페이지로 이동한다.', async () => {
-    await page.getByAltText('유저 아이콘').click();
+    await page.getByAltText('유저 아이콘').click({ timeout: 3000 });
+    await page.getByText('로그인').click({ timeout: 3000 });
     await expect(page).toHaveURL('http://localhost:3000/login');
   });
 
@@ -74,28 +76,15 @@ test.describe('로그인이 되어있는 랜딩페이지', () => {
 
   test.beforeEach(async ({ browser }) => {
     context = await browser.newContext();
-    await context.addCookies([
-      {
-        name: 'accessToken',
-        value: 'testHeader.testPayload,testSignature',
-        domain: 'localhost',
-        path: '/',
-        httpOnly: true,
-        secure: false,
-        sameSite: 'Lax',
-      },
-      {
-        name: 'refreshToken',
-        value: 'testHeader.testPayload,testSignature',
-        domain: 'localhost',
-        path: '/',
-        httpOnly: true,
-        secure: false,
-        sameSite: 'Lax',
-      },
-    ]);
     page = await context.newPage();
+    await page.goto('/login');
+    await page.waitForTimeout(1000);
+    await page.getByTestId('email-input-login').fill('test5@email.com');
+    await page.getByTestId('password-input-login').fill('password1!');
+    await page.getByRole('button', { name: '로그인' }).click();
+    await page.getByRole('button', { name: '확인' }).click();
     await page.goto('/');
+    await page.waitForTimeout(1000);
   });
 
   test.afterEach(async () => {
@@ -112,7 +101,8 @@ test.describe('로그인이 되어있는 랜딩페이지', () => {
   });
 
   test('로그인이 되어 있으면, 유저 아이콘 클릭 시 마이 페이지로 이동한다', async () => {
-    await page.getByAltText('유저 아이콘').click();
+    await page.getByAltText('유저 아이콘').click({ timeout: 3000 });
+    await page.getByText('마이페이지').click({ timeout: 3000 });
     await expect(page).toHaveURL('http://localhost:3000/mypage');
   });
 
diff --git a/src/components/ui/DropdownMenu/index.tsx b/src/components/ui/DropdownMenu/index.tsx
index b424421a..647e3b2a 100644
--- a/src/components/ui/DropdownMenu/index.tsx
+++ b/src/components/ui/DropdownMenu/index.tsx
@@ -1,4 +1,4 @@
-"use client";
+'use client';
 import { useState } from 'react';
 import { cva, type VariantProps } from 'class-variance-authority';
 import { cn } from '@/utils/cn';
@@ -27,34 +27,22 @@ interface DropdownProps extends VariantProps<typeof DropdownVariants> {
   className?: string;
   options?: string[];
   onSelect?: (option: string) => void;
+  trigger?: React.ReactNode;
 }
 
-export default function DropdownMenu({
-  variant,
-  size = 'md',
-  options = ["수정하기", "삭제하기"],
-  onSelect,
-}: DropdownProps) {
+export default function DropdownMenu({ variant, size = 'md', options = ['수정하기', '삭제하기'], onSelect, trigger,  }: DropdownProps) {
   const [isOpen, setIsOpen] = useState(false);
 
   const liPaddingClasses =
-    size === 'md'
-      ? 'py-2 text-md lg:py-4 lg:text-xl'
-      : size === 'sm'
-        ? 'py-2 text-xs lg:py-2 lg:text-md'
-        : size === 'lg'
-          ? 'py-2 text-md lg:py-6 lg:text-2xl'
-          : 'py-2 text-md lg:py-4 lg:text-xl';
+    size === 'md' ? 'py-2 text-md lg:py-4 lg:text-xl' : size === 'sm' ? 'py-2 text-xs lg:py-2 lg:text-md' : size === 'lg' ? 'py-2 text-md lg:py-6 lg:text-2xl' : 'py-2 text-md lg:py-4 lg:text-xl';
 
   return (
-    <div className="relative inline-block ">
-      <button onClick={() => setIsOpen(!isOpen)} className="cursor-pointer">
-        <Image src={dropdownIcon} alt="Dropdown Icon" />
+    <div className='relative inline-block'>
+      <button onClick={() => setIsOpen(!isOpen)} className='cursor-pointer'>
+        {trigger ?? <Image src={dropdownIcon} alt='Dropdown Icon' />}
       </button>
       {isOpen && (
-        <ul className={cn(DropdownVariants({ variant, size }),
-          'top-[45px] right-0'
-        )}>
+        <ul className={cn(DropdownVariants({ variant, size }), 'top-[45px] right-0')}>
           {options.map((option) => (
             <li
               key={option}
@@ -62,11 +50,7 @@ export default function DropdownMenu({
                 setIsOpen(false);
                 onSelect?.(option);
               }}
-              className={cn(
-                liPaddingClasses,
-                'hover:bg-gray-100 cursor-pointer text-center',
-                'text-[var(--color-black-800)]'
-              )}
+              className={cn(liPaddingClasses, 'cursor-pointer text-center hover:bg-gray-100', 'text-[var(--color-black-800)]')}
             >
               {option}
             </li>
diff --git a/src/components/ui/header/LandingHeader.tsx b/src/components/ui/header/LandingHeader.tsx
index 8f85beca..e68fc052 100644
--- a/src/components/ui/header/LandingHeader.tsx
+++ b/src/components/ui/header/LandingHeader.tsx
@@ -1,15 +1,37 @@
 'use client';
+
 import Image from 'next/image';
 import headerLogo from '@/assets/images/headerLogo.svg';
 import userIconDark from '@/assets/icons/userIconDark.svg';
 import searchIcon from '@/assets/icons/searchIcon.svg';
 import Link from 'next/link';
+import DropdownMenu from '@/components/ui/DropdownMenu';
+import { useEffect, useState } from 'react';
+import { getUser } from '@/apis/user';
+import { useHandleHeaderSelect } from '@/hooks/useHandleHeaderSelect';
 
 export interface LandingHeaderProps {
   showIcon?: boolean;
 }
 
 export default function LandingHeader({ showIcon = false }: LandingHeaderProps) {
+  const [isLoggedIn, setIsLoggedIn] = useState(false);
+  const handleSelect = useHandleHeaderSelect();
+
+  useEffect(() => {
+    const checkLogin = async () => {
+      try {
+        const user = await getUser();
+        setIsLoggedIn(!!user);
+      } catch {
+        setIsLoggedIn(false);
+      }
+    };
+    checkLogin();
+  }, []);
+
+  const options = isLoggedIn ? ['마이페이지', '로그아웃'] : ['로그인'];
+
   if (showIcon) {
     return (
       <header className='border-b-line-100 fixed z-100 flex w-full items-center justify-between border-b bg-white px-6 py-2 md:h-[60px] md:px-[72px] md:py-3 xl:h-20 xl:px-[120px] xl:py-4'>
@@ -19,12 +41,11 @@ export default function LandingHeader({ showIcon = false }: LandingHeaderProps)
         <Link href={'/'}>
           <Image src={headerLogo} alt='헤더 로고' className='h-[36px] w-[119px] xl:h-12 xl:w-[172px]' priority />
         </Link>
-        <Link href={'/mypage'}>
-          <Image src={userIconDark} alt='유저 아이콘' />
-        </Link>
+        <DropdownMenu size='sm' options={options} onSelect={handleSelect} trigger={<Image src={userIconDark} alt='유저 아이콘' />} />
       </header>
     );
   }
+
   return (
     <header className='border-b-line-100 fixed z-100 flex w-full items-center justify-center border-b bg-white py-3.5 md:h-[60px] xl:h-20'>
       <Link href={'/'} className='flex items-center justify-center'>
diff --git a/src/components/ui/header/MainHeader.tsx b/src/components/ui/header/MainHeader.tsx
index c8800211..98a26839 100644
--- a/src/components/ui/header/MainHeader.tsx
+++ b/src/components/ui/header/MainHeader.tsx
@@ -8,16 +8,19 @@ import Image from 'next/image';
 import menuIcon from '@/assets/icons/hamburgerMenuIcon.svg';
 import headerLogo from '@/assets/images/headerLogo.svg';
 import SideBar from '../sideBar';
+import DropdownMenu from '@/components/ui/DropdownMenu';
+import { useHandleHeaderSelect } from '@/hooks/useHandleHeaderSelect';
 
 export default function MainHeader() {
   const { data: user, isLoading } = useGetUser();
-
   const [isSidebarOpen, setIsSidebarOpen] = useState(false);
+  const handleSelect = useHandleHeaderSelect();
 
   const toggleSidebar = () => {
     setIsSidebarOpen((prev) => !prev);
   };
 
+
   return (
     <>
       <header className='border-b-line-100 fixed top-0 right-0 left-0 z-10 flex h-13 items-center justify-between border-b-[1px] bg-white px-6 py-4 md:h-15 md:px-18 md:py-[19px] lg:h-20 lg:px-30 lg:py-6.5'>
@@ -33,19 +36,26 @@ export default function MainHeader() {
             검색
           </Link>
         </div>
-        <Link href={'/mypage'} className='flex items-center gap-1.5'>
+        <div>
           {isLoading ? (
-            <>
+            <div className='flex items-center gap-1.5'>
               <div className='h-6 w-6 animate-pulse rounded-full bg-gray-300' />
               <p className='text-sm font-medium text-gray-300'>Loading...</p>
-            </>
+            </div>
           ) : (
-            <>
-              <Avatar src={user?.image} alt='프로필 이미지' className='h-6 w-6' />
-              <p className='text-sm font-medium text-gray-300 lg:text-lg'>{user?.nickname}</p>
-            </>
+            <DropdownMenu
+              size='sm'
+              options={['마이페이지', '로그아웃']}
+              onSelect={handleSelect}
+              trigger={
+                <div className='flex items-center gap-1.5 cursor-pointer'>
+                  <Avatar src={user?.image} alt='프로필 이미지' className='h-6 w-6' />
+                  <p className='text-sm font-medium text-gray-300 lg:text-lg'>{user?.nickname}</p>
+                </div>
+              }
+            />
           )}
-        </Link>
+        </div>
       </header>
 
       {isSidebarOpen && (
diff --git a/src/hooks/useHandleHeaderSelect.ts b/src/hooks/useHandleHeaderSelect.ts
new file mode 100644
index 00000000..667597f3
--- /dev/null
+++ b/src/hooks/useHandleHeaderSelect.ts
@@ -0,0 +1,26 @@
+'use client';
+
+import { useRouter } from 'next/navigation';
+import logout from '@/actions/logoutAction';
+
+export const useHandleHeaderSelect = () => {
+  const router = useRouter();
+
+  const handleSelect = async (option: string) => {
+    if (option === '마이페이지') {
+      router.push('/mypage');
+    } else if (option === '로그아웃') {
+      const result = await logout();
+      if (result.status) {
+        router.push('/login');
+      } else {
+        console.error(result.error);
+        alert(result.error);
+      }
+    } else if (option === '로그인') {
+      router.push('/login');
+    }
+  };
+
+  return handleSelect;
+};


Reviews:
user: login: rak517
body: 바뀐 부분들과 임시로 처리해놓으신 E2E 코드까지 확인했습니다 고생하셨습니다!
문제 없는거 같아서 승인해둘게요!
state: APPROVED
author_association: OWNER
submitted_at: 2025-04-07T04:05:36Z
user: login: ToKyun02
body: 작업하시느라 고생하셨습니다!

파이어폭스에서 안되는 원인을 재현해보려고 제 로컬에서도 실행해봤는데, 오류없이 동작했습니다. 아마 파이어폭스 환경에서 다른 엔진보다 늦게 실행되는 것이 원인인 것 같아서 버튼 클릭 동작에 timeout을 추가하면 될 것 같습니다.

또한, 로그인 이후 랜딩 페이지의 beforeEach는 실제 로그인으로 처리하는 것이 좋을 것 같아서 다음처럼 수정하면 좋을 것 같습니다 :)

```js
test.beforeEach(async ({ browser }) => {
    context = await browser.newContext();
    page = await context.newPage();
    await page.goto('/login');
    await page.waitForTimeout(1000);
    await page.getByTestId('email-input-login').fill('test5@email.com');
    await page.getByTestId('password-input-login').fill('password1!');
    await page.getByRole('button', { name: '로그인' }).click();
    await page.getByRole('button', { name: '확인' }).click();
    await page.goto('/');
    await page.waitForTimeout(1000);
  });
```
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T04:47:20Z

Review comments:
diff_hunk: @@ -28,10 +28,10 @@ test.describe('랜딩 페이지', () => {
     await expect(page).toHaveURL('http://localhost:3000');
   });
 
-  test('로그인이 안되어 있으면, 유저 아이콘 클릭 시 로그인 페이지로 이동한다.', async () => {
-    await page.getByAltText('유저 아이콘').click();
-    await expect(page).toHaveURL('http://localhost:3000/login');
-  });
+  //test('로그인이 안되어 있으면, 유저 아이콘 클릭 시 로그인 페이지로 이동한다.', async () => {
+   // await page.getByAltText('유저 아이콘').click();
+    //await expect(page).toHaveURL('http://localhost:3000/login');
+  //});
path: e2e/landing.spec.ts
user: login: ToKyun02
body: 아래처럼 작성하시면 테스트 통과할 것 같습니다 :)
 ```js
 test('로그인이 안되어 있으면, 유저 아이콘 클릭 시 로그인 페이지로 이동한다.', async () => {
    await page.getByAltText('유저 아이콘').click({ timeout: 3000 });
    await page.getByText('로그인').click({ timeout: 3000 });
    await expect(page).toHaveURL('http://localhost:3000/login');
  });
```

created_at: 2025-04-07T04:45:51Z
author_association: COLLABORATOR
diff_hunk: @@ -111,10 +111,10 @@ test.describe('로그인이 되어있는 랜딩페이지', () => {
     await expect(page).toHaveURL('http://localhost:3000/search');
   });
 
-  test('로그인이 되어 있으면, 유저 아이콘 클릭 시 마이 페이지로 이동한다', async () => {
-    await page.getByAltText('유저 아이콘').click();
-    await expect(page).toHaveURL('http://localhost:3000/mypage');
-  });
+  //test('로그인이 되어 있으면, 유저 아이콘 클릭 시 마이 페이지로 이동한다', async () => {
+    //await page.getByAltText('유저 아이콘').click();
+    //await expect(page).toHaveURL('http://localhost:3000/mypage');
+  //});
path: e2e/landing.spec.ts
user: login: ToKyun02
body: 아래처럼 작성하시면 테스트 통과할 것 같습니다 :)

```js
test('로그인이 되어 있으면, 유저 아이콘 클릭 시 마이 페이지로 이동한다', async () => {
    await page.getByAltText('유저 아이콘').click({ timeout: 3000 });
    await page.getByText('마이페이지').click({ timeout: 3000 });
    await expect(page).toHaveURL('http://localhost:3000/mypage');
  });
```
created_at: 2025-04-07T04:46:21Z
author_association: COLLABORATOR

PR comments:
user: login: vercel[bot]
created_at: 2025-04-04T14:41:10Z
body: [vc]: #B9zRKhlY8awYeS3Kag+r57bkN0uqr9wEC5QBj/+PZTI=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJlcGlncmFtIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImVwaWdyYW0tZ2l0LXJlZmFjdG9yLWVwLTEyMGhlYWRlci1sb2dvdXQtcmFrNTE3cy1wcm9qZWN0cy52ZXJjZWwuYXBwIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9yYWs1MTdzLXByb2plY3RzL2VwaWdyYW0vQlFrZGUyVlhRRURyQkRmejVzanF5V3l6UUgzRCIsInByZXZpZXdVcmwiOiJlcGlncmFtLWdpdC1yZWZhY3Rvci1lcC0xMjBoZWFkZXItbG9nb3V0LXJhazUxN3MtcHJvamVjdHMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsInJvb3REaXJlY3RvcnkiOm51bGx9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **epigram** | ✅ Ready ([Inspect](https://vercel.com/rak517s-projects/epigram/BQkde2VXQEDrBDfz5sjqyWyzQH3D)) | [Visit Preview](https://epigram-git-refactor-ep-120header-logout-rak517s-projects.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/epigram-git-refactor-ep-120header-logout-rak517s-projects.vercel.app?via=pr-comment-feedback-link) | Apr 7, 2025 10:29am |


author_association: NONE
user: login: jaehyeongjung
created_at: 2025-04-07T10:17:41Z
body: > 작업하시느라 고생하셨습니다!
> 
> 파이어폭스에서 안되는 원인을 재현해보려고 제 로컬에서도 실행해봤는데, 오류없이 동작했습니다. 아마 파이어폭스 환경에서 다른 엔진보다 늦게 실행되는 것이 원인인 것 같아서 버튼 클릭 동작에 timeout을 추가하면 될 것 같습니다.
> 
> 또한, 로그인 이후 랜딩 페이지의 beforeEach는 실제 로그인으로 처리하는 것이 좋을 것 같아서 다음처럼 수정하면 좋을 것 같습니다 :)
> 
> ```js
> test.beforeEach(async ({ browser }) => {
>     context = await browser.newContext();
>     page = await context.newPage();
>     await page.goto('/login');
>     await page.waitForTimeout(1000);
>     await page.getByTestId('email-input-login').fill('test5@email.com');
>     await page.getByTestId('password-input-login').fill('password1!');
>     await page.getByRole('button', { name: '로그인' }).click();
>     await page.getByRole('button', { name: '확인' }).click();
>     await page.goto('/');
>     await page.waitForTimeout(1000);
>   });
> ```

넵 감사합니다 도균님 
author_association: COLLABORATOR