인스타그램 클론 프로젝트 J-Stagram 의 피드 형식 게시글 기능을 만드는 과정에서, 게시글 리스트를 불러오는 것까지 완성 후 해당 게시글의 작성자를 불러오는 작업을 진행중이었다. 이를 Supabase 의 Admin API 인
listUsers()
를 통해 현재 가입된 모든 사용자 리스트를 불러왔다. 그 이후 게시글의user_id
(supabase 의 User 와 연결됨) 에 해당하는 유저를 찾아 작성자의 프로필 사진 및 이름을 담은 user 정보 객체를 담고자 했다.
export async function getPosts({ searchInput = '' }): Promise<PostWithImages[]> {
const supabase = await createServerSupabaseClient();
const { data: posts, error } = await supabase
.from('posts')
.select(
`
id,
title,
content,
user_id,
created_at,
images (url)
`,
)
.eq('is_public', true)
.like('title', `%${searchInput}%`)
.order('created_at', { ascending: true });
if (error) handleError(error);
// Admin client 사용
const supabaseAdmin = await createServerSupabaseAdminClient();
const { data: allUsers, error: usersError } = await supabaseAdmin.auth.admin.listUsers();
if (usersError) handleError(usersError);
...
}
listUsers()
를 통해 모든 사용자 데이터를 allUsers
라는 이름으로 가져왔는데, 이를 posts 데이터를 return 하기 전에 user_info
형태로 끼워줄 생각이었다.
이때 find
메서드를 사용하려다, 커서를 활용하여 더 좋은 방법이 없는지 검색해보니 Map
객체를 활용하면 더 좋을 것이라는 내용이 있었다.
MDN 문서에 따르면, Map
은 키와 값 쌍을 저장하고, 넣은 순서를 기억하며 숫자, 문자, 객체 등 어떤 값도 키로 쓸 수 있는 객체라고 한다. 즉 효율 좋고 빠른 키-값 저장소로, id → 객체 형태로 매핑할 수 있는 훨씬 깔끔한 구조를 만들 수 있었다.
const userMap = new Map(
allUsers.users.map((u) => [
u.id,
{
email: u.email,
user_metadata: u.user_metadata,
},
]),
);
return posts.map((post) => ({
...post,
images: post.images ?? [],
user_info: userMap.get(post.user_id) ?? null,
}));
Map 을 사용하면 위와 같이 유저 데이터를 매핑 할 수 있다. get 은 Map이나 객체에서 특정 키에 해당하는 값을 가져오는 메서드인데, 이를 통해 게시글의 작성자 user_id
로 키 값인 user 의 id 와 바로 대조하여 값을 찾을 수 있다.
결과를 봤을 때, posts 의 각 post 마다 해당하는 유저 데이터가 잘 삽입이 된 것을 확인할 수 있었다.
{
"id": 2,
"title": "게시글1",
"content": "게시글 내용입니드아아앙",
"user_id": "e007aca1-3f90-48fe-acaf-7d6ee201e56a",
"created_at": "2025-09-12T07:40:11.341386+00:00",
"images": [
{
"url": "https://ejxzslfkaxsbcabuiqzs.supabase.co/storage/v1/object/public/gallery/IMG_2576.JPG"
},
{
"url": "https://ejxzslfkaxsbcabuiqzs.supabase.co/storage/v1/object/public/gallery/IMG_5620.jpg"
}
],
"user_info": {
"email": "konnimey@naver.com",
"user_metadata": {
"avatar_url": "http://k.kakaocdn.net/dn/bvW2Ym/btsQcnFilfU/tDTGJkVGS1ps69BHoAHwr1/img_640x640.jpg",
"email": "konnimey@naver.com",
"email_verified": true,
"full_name": "혜진",
"iss": "https://kapi.kakao.com",
"name": "혜진",
"phone_verified": false,
"preferred_username": "혜진",
"provider_id": "4363702432",
"sub": "4363702432",
"user_name": "혜진"
}
}
}
사용자의 프로필 사진과 이름이 잘 뜨는 것도 확인할 수 있었다
new Map
과 find
는 둘 다 "특정 데이터를 찾아내는 역할"을 할 수 있는데, 내부 동작 방식이 달라서 성능 차이가 있다.
find
배열에서 조건에 맞는 첫 번째 요소를 찾는 메서드
const users = [
{ id: '1', name: '철수' },
{ id: '2', name: '영희' },
{ id: '3', name: '민수' },
];
// id가 '2'인 사용자 찾기
const user = users.find((u) => u.id === '2');
console.log(user); // { id: '2', name: '영희' }
이는 조건에 맞는 요소를 찾을 때까지 배열을 앞에서부터 끝까지 검사하며, 최악의 경우 배열 전체를 다 돌아야 한다. 지금같은 사이드 프로젝트에서처럼 사용자 데이터가 적을 때는 괜찮지만, 이후 실제 운영업무를 수행할 때 데이터가 수천~수만 건 이상이되면 성능이 저하될 우려가 있다.
Map
키-값 쌍을 저장하는 자료구조이며, 키로 바로 접근이 가능하다.
const userMap = new Map([
['1', { id: '1', name: '철수' }],
['2', { id: '2', name: '영희' }],
['3', { id: '3', name: '민수' }],
]);
const user = userMap.get('2');
console.log(user); // { id: '2', name: '영희' }
get()
을 사용하면 바로 해당하는 값을 찾으며, 데이터가 많아져도 성능이 거의 일정하다. 다만 find
처럼 조건부 탐색(ex. "name이 영희인 사용자")에는 적합하지 않다는 단점이 있다.
두 개념 모두 장단점이 있지만, find
의 경우엔 매번 전체 배열을 돌면서 찾아야 하기 때문에 성능이 나빠지므로 조건이 붙지 않는 탐색의 경우엔 Map
객체를 적극 사용하는 것이 좋을 것 같다..!