React
[React] useEffect 안에서 비동기 함수 사용하기
수방방
2024. 7. 29. 01:24
📍 useEffect 안에 비동기 함수 사용하기
- useEffect 훅은 반환값으로 클린업(clean-up) 함수를 반환할 수 있습니다.
- 이 클린업 함수는 컴포넌트가 언마운트되거나 의존성 배열이 변경될 때 실행됩니다.
- 클린업 함수는 자원을 해제하거나, 구독을 해지하는 등의 작업을 수행하는 데 유용합니다.
- 반면, async 함수는 항상 Promise 객체를 반환합니다.
- await 키워드는 async 함수 내부에서 사용되며, Promise가 해결될 때까지 함수의 실행을 일시 중지합니다.
- Promise가 해결되면, await는 해당 Promise의 결과 값을 반환합니다.
- `useEffect`는 직접적으로 비동기 함수를 반환할 수 없기 때문에, 비동기 작업을 처리할 때는 내부에 즉시 실행 함수(IIFE)를 사용하거나, 별도의 비동기 함수를 정의하고 호출하는 방식을 사용합니다.
- 예를 들어, 다음은 Supabase를 사용하여 로그인된 사용자 정보를 가져오는 방법입니다.
useEffect(() => {
(async () => {
const user = await supabase.auth.getUser();
setUserResponse(user);
})();
}, []);
- 이 코드에서 `useEffect`는 컴포넌트가 마운트될 때 실행됩니다.
- 훅 내부에서 정의된 즉시 실행 함수(IIFE)는 비동기 함수로, `supabase.auth.getUser` 를 호출하여 사용자의 로그인 정보를 가져옵니다.
- 이 비동기 함수는 Promise 를 반환하며, `await` 키워드를 사용해 비동기 호출의 결과를 기다립니다.
📍 왜 useEffect 안에 비동기 함수를 사용했을까?
import Input from '@/components/Input';
import { createClient } from '@/utils/supabase/client';
import { UserResponse } from '@supabase/supabase-js';
import { useRouter } from 'next/navigation';
import { useEffect, useRef, useState } from 'react';
const supabase = createClient();
export default function Admin() {
const router = useRouter();
const [userResponse, setUserResponse] = useState<UserResponse>();
const emailRef = useRef<HTMLInputElement>(null);
const passwordRef = useRef<HTMLInputElement>(null);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const response = await supabase.auth.signInWithPassword({
email: emailRef.current?.value ?? '',
password: passwordRef.current?.value ?? '',
});
if (!response.data.user) {
return alert('로그인에 실패하였습니다.');
}
router.refresh(); // 현재 라우터를 새로고침, 서버에 새 요청을 보내고 데이터 요청을 다시 가져오며, 서버 컴포넌트를 다시 렌더링
};
useEffect(() => {
(async () => {
const user = await supabase.auth.getUser();
setUserResponse(user);
})();
}, []);
return (
<div className="container mx-auto flex flex-col px-4 pb-20 pt-12">
{!!userResponse?.data.user ? (
<>
<div className="flex flex-col gap-2">
<div className="mb-8">
<b>{userResponse.data.user.email}</b> 님으로 로그인하셨습니다.
</div>
</div>
<button
type="button"
className="w-full rounded-md bg-gray-800 py-2 text-white"
onClick={() => router.push('/write')}
>
글 쓰러 가기
</button>
<button
type="button"
className="mt-4 w-full rounded-md bg-gray-800 py-2 text-white"
onClick={() => {
supabase.auth.signOut();
router.push('/');
}}
>
로그아웃
</button>
</>
) : (
<div className="flex flex-col gap-8">
<h1 className="text-2xl font-medium">관리자 로그인</h1>
<form onSubmit={handleSubmit}>
<div className="flex flex-col gap-3">
<Input type="text" placeholder="이메일" ref={emailRef} />
<Input type="password" placeholder="비밀번호" ref={passwordRef} />
</div>
<button
type="submit"
className="mt-4 w-full rounded-md bg-gray-800 py-2 text-white"
>
로그인
</button>
</form>
</div>
)}
</div>
);
}
- 위 코드에서 보시다시피, `useEffect` 안에 `getUser()`를 호출하는 이유는 컴포넌트가 마운트될 때 사용자의 로그인 상태를 확인하고 그에 따라 적절한 UI를 렌더링하기 위함입니다.
- 페이지가 로드될 때, `useEffect`가 실행되어 현재 로그인된 사용자의 정보를 가져와 상태를 업데이트합니다.
- `handleSubmit` 함수는 로그인 폼 제출 시 호출되며, 로그인 성공 후 `router.refresh()`를 통해 페이지를 새로고침합니다.
- 이 과정에서 컴포넌트가 다시 마운트되어 `useEffect`가 다시 실행 되고, 최신 사용자 정보를 가져옵니다.