Type something to search...

06 6 logout

1-Profile.tsx

1-1-UI요소 추가

1-1-1-프로필 이미지가 없을 경우 사용자명 렌더링하기

  1. next-with-supabase\components\Profile.tsx
  2. Image 컴포넌트를 수정한다
1
**...
2
**</Link>
3
) : (**
4
****<>
5
{data?.image_url ? (
6
<Image src={data.image_url || ''} alt={data.display_name || ''} width={50} height={50} className="rounded-full" />
7
) : (
8
<div className="h-[50px] w-[50px] flex items-center justify-center">
9
<h1>{data.email}</h1>
10
</div>
11
)}
12
</>****
13
...**
  1. 테스트
  2. 이미지 데이터가 null이면 email 이 렌더링된다.

2-Logout

2-1-Logout버튼만들기

2-1-1-components\Profile.tsx

  1. createClient함수 임포트 handleLogout 작성
import { createClient } from "@/lib/supabase/client";
...
const handleLogout = () => {
const supabase = createClient();
};
  1. useQueryClient
1
"use client";
2
import React from "react";
3
import { Button } from "./ui/button";
4
import Link from "next/link";
5
import useUser from "@/app/hook/useUser";
6
import Image from "next/image";
7
import { createClient } from "@/lib/supabase/client";
8
import { useQueryClient } from "@tanstack/react-query";
9
10
const Profile = () => {
11
const { isFetching, data } = useUser();
12
const queryClient = useQueryClient();
13
if (isFetching) {
14
return <></>;
15
}
16
const handleLogout = async () => {
17
const supabase = createClient();
18
queryClient.clear();
19
await supabase.auth.signOut();
20
};
21
return (
22
<div>
23
{!data?.id ? (
24
<Link href="/auth">
25
<Button variant="outline">SignIn</Button>
26
</Link>
27
) : (
28
<>
29
{data?.image_url ? (
30
<div className="flex flex-col justify-center items-center">
31
<Image src={data.image_url || ""} alt={data.display_name || ""} width={50} height={50} className="rounded-full" />
32
{data.display_name}
33
</div>
34
) : (
35
<div className="h-[50px] w-[50px] flex items-center justify-center">
36
<h1>{data.email}</h1>
37
</div>
38
)}
39
</>
40
// 1:35
41
)}
42
</div>
43
);
44
};
45
export default Profile;
  1. router
1
import { useRouter } from "next/navigation";
2
const Profile = () => {
3
...
4
const router = useRouter();
5
...
6
}
7
const handleLogout = async () => {
8
...
9
router.refresh();
10
}

2-미들웨어 작성

정보

💡 미들웨어는 서버와 클라이언트 사이에서 로그인 정보를 안전하게 관리하는 중개자 역할을 한다.

🔹 클라이언트(사용자)가 페이지에 접근하면middleware.ts가 먼저 요청을 가로채서 로그인 상태를 확인 🔹 로그인되어 있으면 → 그냥 원래 가려던 페이지로 이동! 🔹 로그인 안 되어 있으면/login 페이지로 강제 이동! 🔹 세션 정보를 유지하면서 쿠키도 관리해서, 사용자가 로그아웃되거나 세션이 만료되면 다시 로그인하게 유도한다. 즉, “이 사용자가 로그인된 상태인지 확인하고, 필요한 경우 로그인 페이지로 보내는 보안 게이트” 같은 역할을 한다. 1.

1
<div className="flex flex-col justify-center items-center">
2
<Image src={data.image_url || ""} alt={data.display_name || ""} width={50} height={50} className="rounded-full** cursor-pointer" onClick={handleLogout}/**>
3
{data.display_name}
4
</div>
  1. api docs 참조 https://supabase.com/docs/guides/auth/server-side/nextjs 4단계의 middleware Hook을 생성한다
  2. middleware.ts 파일생성
1
import { type NextRequest } from 'next/server'
2
import { updateSession } from '@/utils/supabase/middleware'
3
4
export async function middleware(request: NextRequest) {
5
return await updateSession(request)
6
}
7
8
export const config = {
9
matcher: [
10
/*
11
* Match all request paths except for the ones starting with:
12
* - _next/static (static files)
13
* - _next/image (image optimization files)
14
* - favicon.ico (favicon file)
15
* Feel free to modify this pattern to include more paths.
16
*/
17
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
18
],
19
}
  1. utils/supabase/ middleware.ts 파일생성 후 코드 복붙
1
import { createServerClient } from '@supabase/ssr'
2
import { NextResponse, type NextRequest } from 'next/server'
3
4
export async function updateSession(request: NextRequest) {
5
let supabaseResponse = NextResponse.next({
6
request,
7
})
8
9
const supabase = createServerClient(
10
process.env.NEXT_PUBLIC_SUPABASE_URL!,
11
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
12
{
13
cookies: {
14
getAll() {
15
return request.cookies.getAll()
16
},
17
setAll(cookiesToSet) {
18
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
19
supabaseResponse = NextResponse.next({
20
request,
21
})
22
cookiesToSet.forEach(({ name, value, options }) =>
23
supabaseResponse.cookies.set(name, value, options)
24
)
25
},
26
},
27
}
28
)
29
30
// Do not run code between createServerClient and
31
// supabase.auth.getUser(). A simple mistake could make it very hard to debug
32
// issues with users being randomly logged out.
33
34
// IMPORTANT: DO NOT REMOVE auth.getUser()
35
36
const {
37
data: { user },
38
} = await supabase.auth.getUser()
39
40
if (
41
!user &&
42
!request.nextUrl.pathname.startsWith('/login') &&
43
!request.nextUrl.pathname.startsWith('/auth')
44
) {
45
// no user, potentially respond by redirecting the user to the login page
46
const url = request.nextUrl.clone()
47
url.pathname = '/login'
48
return NextResponse.redirect(url)
49
}
50
51
// IMPORTANT: You *must* return the supabaseResponse object as it is.
52
// If you're creating a new response object with NextResponse.next() make sure to:
53
// 1. Pass the request in it, like so:
54
// const myNewResponse = NextResponse.next({ request })
55
// 2. Copy over the cookies, like so:
56
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
57
// 3. Change the myNewResponse object to fit your needs, but avoid changing
58
// the cookies!
59
// 4. Finally:
60
// return myNewResponse
61
// If this is not done, you may be causing the browser and server to go out
62
// of sync and terminate the user's session prematurely!
63
64
return supabaseResponse
65
}

  1. ./middleware.ts 수정
1
export async function middleware(request: NextRequest) {
2
const url = new URL(request.url);
3
console.log("접속 경로:", url.pathname);
4
...

콘솔에 pathname이 확인된다

  1. Supabase 서버 클라이언트 생성

https://supabase.com/docs/guides/auth/server-side/nextjs 표시된 부분을 복붙하고 필요없는 코드는 삭제한다

createServerClient()는 Supabase의 클라이언트 라이브러리에서 서버 측 클라이언트를 생성하는 함수입니다. 이 함수는 Supabase 프로젝트와의 연결을 설정하고, 데이터베이스와의 상호작용을 가능하게 합니다. 일반적으로 이 함수는 서버 환경에서 사용되며, 인증된 요청을 처리할 수 있도록 설정됩니다.

  • supabaseUrl: Supabase 프로젝트의 URL입니다.
  • supabaseKey: 인증을 위한 키로, 서비스 역할 키 또는 익명 키를 사용할 수 있습니다.
  • createServerClient()를 호출하면 Supabase 클라이언트 인스턴스가 생성되어 데이터베이스와의 상호작용을 수행할 수 있습니다. 이 클라이언트를 사용하여 데이터베이스 쿼리, 인증, 스토리지 작업 등을 수행할 수 있습니다.
1
import { createServerClient, type CookieOptions } from "@supabase/ssr";
2
import { NextResponse, type NextRequest } from "next/server";
3
import { protectedPaths } from "./lib/constant";
4
5
export async function middleware(request: NextRequest) {
6
let response = NextResponse.next({
7
request: {
8
headers: request.headers,
9
},
10
});
11
12
const supabase = createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, {
13
cookies: {
14
get(name: string) {
15
return request.cookies.get(name)?.value;
16
},
17
set(name: string, value: string, options: CookieOptions) {
18
request.cookies.set({
19
name,
20
value,
21
...options,
22
});
23
response = NextResponse.next({
24
request: {
25
headers: request.headers,
26
},
27
});
28
response.cookies.set({
29
name,
30
value,
31
...options,
32
});
33
},
34
remove(name: string, options: CookieOptions) {
35
request.cookies.set({
36
name,
37
value: "",
38
...options,
39
});
40
response = NextResponse.next({
41
request: {
42
headers: request.headers,
43
},
44
});
45
response.cookies.set({
46
name,
47
value: "",
48
...options,
49
});
50
},
51
},
52
});
53
54
const { data } = await supabase.auth.getSession();
55
const url = new URL(request.url);
56
if (data.session) {
57
if (url.pathname === "/auth") {
58
return NextResponse.redirect(new URL("/", request.url));
59
}
60
return response;
61
} else {
62
if (protectedPaths.includes(url.pathname)) {
63
return NextResponse.redirect(new URL("/auth?next=" + url.pathname, request.url));
64
}
65
return response;
66
}
67
}
68
69
export const config = {
70
matcher: [
71
/*
72
* Match all request paths except for the ones starting with:
73
* - _next/static (static files)
74
* - _next/image (image optimization files)
75
* - favicon.ico (favicon file)
76
* Feel free to modify this pattern to include more paths.
77
*/
78
"/((?!_next/static|_next/image|favicon.ico).*)",
79
],
80
};

2-1-constant 설정

  1. next-with-supabase\lib\constant\index.ts 파일생성
1
export const protectedPaths = ["/dashboard", "/profile"];
  1. dashboard 와 profile 이동시 아래와 같이 라우팅 된다.

3-Profile수정

  1. handleLogout 함수에 router 추가
1
...
2
import { **usePathname**, useRouter } from "next/navigation";
3
**import { protectedPaths } from "@/lib/constant";**
4
...
5
const Profile = () => {
6
...
7
** const pathname = usePathname();**
8
const handleLogout = async () => {
9
...
10
router.refresh();
11
** if (protectedPaths.includes(pathname)) {
12
router.replace(`/auth?next=${pathname}`);
13
}**
14
};