Skip to main content

Authentication

๋ณดํ†ต ์ธ์ฆ์€ ์„ธ ๊ฐ€์ง€ ์ฃผ์š” ๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค:

  1. ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋กœ๊ทธ์ธ ์ •๋ณด(์•„์ด๋””, ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ)์„ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋กœ ํ•ด๋‹น ๋กœ๊ทธ์ธ ์ •๋ณด์„ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
  3. ์ธ์ฆ ํ›„ ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ์„ ์ €์žฅํ•˜์—ฌ ์ดํ›„ ์š”์ฒญ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ์ •๋ณด ์ˆ˜์ง‘ ๋ฐฉ๋ฒ•โ€‹

์•ฑ์—์„œ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ์— OAuth๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, OAuth ์ œ๊ณต์ž์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 3๋‹จ๊ณ„๋กœ ๋ฐ”๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ „์šฉ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐโ€‹

์›น์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŽ˜์ด์ง€๋“ค์€ ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ˆœํ•˜์—ฌ ๋ณ„๋„์˜ ๋ณต์žกํ•œ ๋ถ„ํ•ด ์ž‘์—…์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ๋กœ๊ทธ์ธ๊ณผ ํšŒ์›๊ฐ€์ž… ์–‘์‹์€ ์™ธํ˜•์ด ๋น„์Šทํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋‘ ์–‘์‹์„ ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€์—์„œ ํ†ตํ•ฉํ•˜์—ฌ ์ œ๊ณตํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

  • ๐Ÿ“‚ pages
    • ๐Ÿ“‚ login
      • ๐Ÿ“‚ ui
        • ๐Ÿ“„ LoginPage.tsx (or your framework's component file format)
        • ๐Ÿ“„ RegisterPage.tsx
      • ๐Ÿ“„ index.ts
    • other pagesโ€ฆ

๋กœ๊ทธ์ธ๊ณผ ํšŒ์›๊ฐ€์ž… ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ„๋„๋กœ ๋งŒ๋“ค๊ณ , ํ•„์š”์— ๋”ฐ๋ผ index ํŒŒ์ผ์—์„œ export ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋กœ๊ทธ์ธ ์ •๋ณด์„ ์ž…๋ ฅ๋ฐ›๋Š” ํผ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๋กœ๊ทธ์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋งŒ๋“ค๊ธฐโ€‹

์•ฑ์˜ ์–ด๋””์„œ๋‚˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ๊ทธ์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด, ์ด ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์œ„์ ฏ์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ์„ธ๋ถ„ํ™”๋ฅผ ํ”ผํ•˜๋ฉด์„œ๋„ ์–ด๋–ค ํŽ˜์ด์ง€์—์„œ๋‚˜ ์‰ฝ๊ฒŒ ๋กœ๊ทธ์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๐Ÿ“‚ widgets
    • ๐Ÿ“‚ login-dialog
      • ๐Ÿ“‚ ui
        • ๐Ÿ“„ LoginDialog.tsx
      • ๐Ÿ“„ index.ts
    • other widgetsโ€ฆ

๊ฐ€์ด๋“œ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ์ „์šฉ ํŽ˜์ด์ง€ ๋ฐฉ์‹์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๋™์ผํ•œ ์›์น™์„ ๋กœ๊ทธ์ธ ๋‹ค์ด์–ผ๋กœ๊ทธ์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ์ธก ๊ฒ€์ฆโ€‹

ํŠนํžˆ ํšŒ์›๊ฐ€์ž…์˜ ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋‚ด์šฉ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๋•Œ ๋น ๋ฅด๊ฒŒ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ํด๋ผ์ด์–ธํŠธ ์ธก ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์˜ model ์„ธ๊ทธ๋จผํŠธ์—์„œ ๊ฒ€์ฆ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด JS/TS์—์„œ๋Š” Zod์™€ ๊ฐ™์€ ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

pages/login/model/registration-schema.ts
import { z } from "zod";

export const registrationData = z.object({
email: z.string().email(),
password: z.string().min(6),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค",
path: ["confirmPassword"],
});

๊ทธ๋Ÿฐ ๋‹ค์Œ, ui ์„ธ๊ทธ๋จผํŠธ์—์„œ ์ด ์Šคํ‚ค๋งˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

pages/login/ui/RegisterPage.tsx
import { registrationData } from "../model/registration-schema";

function validate(formData: FormData) {
const data = Object.fromEntries(formData.entries());
try {
registrationData.parse(data);
} catch (error) {
// TODO: Show error message to the user
}
}

export function RegisterPage() {
return (
<form onSubmit={(e) => validate(new FormData(e.target))}>
<label htmlFor="email">์ด๋ฉ”์ผ</label>
<input id="email" name="email" required />

<label htmlFor="password">๋น„๋ฐ€๋ฒˆํ˜ธ (์ตœ์†Œ 6์ž)</label>
<input id="password" name="password" type="password" required />

<label htmlFor="confirmPassword">๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ</label>
<input id="confirmPassword" name="confirmPassword" type="password" required />
</form>
)
}

๋กœ๊ทธ์ธ ์ •๋ณด ์ „์†ก ๋ฐฉ๋ฒ•โ€‹

๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๊ธฐ ์œ„ํ•œ ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. ์ด ํ•จ์ˆ˜๋Š” ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ๋ฎคํ…Œ์ด์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์˜ˆ: TanStack Query)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์ฒญ ํ•จ์ˆ˜ ์ €์žฅ ์œ„์น˜โ€‹

์ด ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜๋Š” ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค: shared/api ๋˜๋Š” ํŽ˜์ด์ง€์˜ api ์„ธ๊ทธ๋จผํŠธ์ž…๋‹ˆ๋‹ค.

shared/api์— ์ €์žฅํ•˜๊ธฐโ€‹

๋ชจ๋“  API ์š”์ฒญ์„ shared/api์— ๋ชจ์•„์„œ ๊ด€๋ฆฌํ•˜๊ณ , ์—”๋“œํฌ์ธํŠธ๋ณ„๋กœ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ํŒŒ์ผ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ๐Ÿ“‚ shared
    • ๐Ÿ“‚ api
      • ๐Ÿ“‚ endpoints
        • ๐Ÿ“„ login.ts
        • other endpoint functionsโ€ฆ
      • ๐Ÿ“„ client.ts
      • ๐Ÿ“„ index.ts

๐Ÿ“„ client.ts ํŒŒ์ผ์€ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์›์‹œ ํ•จ์ˆ˜(์˜ˆ: fetch())์— ๋Œ€ํ•œ ๋ž˜ํผ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ž˜ํผ๋Š” ๋ฐฑ์—”๋“œ์˜ ๊ธฐ๋ณธ URL ์„ค์ •, ํ—ค๋” ์„ค์ •, ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™” ๋“ฑ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

shared/api/endpoints/login.ts
import { POST } from "../client";

export function login({ email, password }: { email: string, password: string }) {
return POST("/login", { email, password });
}
shared/api/index.ts
export { login } from "./endpoints/login";

ํŽ˜์ด์ง€์˜ api ์„ธ๊ทธ๋จผํŠธ์— ์ €์žฅํ•˜๊ธฐโ€‹

๋กœ๊ทธ์ธ ์š”์ฒญ์ด ํŠน์ • ํŽ˜์ด์ง€์—๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์˜ api ์„ธ๊ทธ๋จผํŠธ์— ํ•จ์ˆ˜๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ๐Ÿ“‚ pages
    • ๐Ÿ“‚ login
      • ๐Ÿ“‚ api
        • ๐Ÿ“„ login.ts
      • ๐Ÿ“‚ ui
        • ๐Ÿ“„ LoginPage.tsx
      • ๐Ÿ“„ index.ts
    • other pagesโ€ฆ
pages/login/api/login.ts
import { POST } from "shared/api";

export function login({ email, password }: { email: string, password: string }) {
return POST("/login", { email, password });
}

์ด ํ•จ์ˆ˜๋Š” ํŽ˜์ด์ง€์˜ ๊ณต๊ฐœ API์—์„œ ๋‚ด๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ์š”์ฒญ์ด ๋‹ค๋ฅธ ๊ณณ์—์„œ ํ•„์š”ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋‚ฎ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด์ค‘ ์ธ์ฆ(2FA)โ€‹

์•ฑ์ด ์ด์ค‘ ์ธ์ฆ(2FA)์„ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์ผํšŒ์šฉ ๋น„๋ฐ€๋ฒˆํ˜ธ(OTP)๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ณ„๋„์˜ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ POST /login ์š”์ฒญ์€ ์‚ฌ์šฉ์ž๊ฐ€ 2FA๋ฅผ ํ™œ์„ฑํ™”ํ–ˆ์Œ์„ ๋‚˜ํƒ€๋‚ด๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ ํฌํ•จ๋œ ์‚ฌ์šฉ์ž ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ”Œ๋ž˜๊ทธ๊ฐ€ ์„ค์ •๋˜๋ฉด ์‚ฌ์šฉ์ž๋ฅผ 2FA ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋””๋ ‰์…˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2FA ํŽ˜์ด์ง€๋Š” ๋กœ๊ทธ์ธ๊ณผ ๋ฐ€์ ‘ํ•˜๊ฒŒ ์—ฐ๊ด€๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ Pages ๋ ˆ์ด์–ด์˜ login ์Šฌ๋ผ์ด์Šค์— ํ•จ๊ป˜ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ด์ค‘ ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” login() ํ•จ์ˆ˜์™€ ์œ ์‚ฌํ•œ ๋˜ ๋‹ค๋ฅธ ์š”์ฒญ ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ•จ์ˆ˜๋“ค์€ Shared๋‚˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์˜ api ์„ธ๊ทธ๋จผํŠธ์— ํ•จ๊ป˜ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ธ์ฆ๋œ ์š”์ฒญ์˜ ํ† ํฐ ์ €์žฅ ๋ฐฉ๋ฒ•โ€‹

์ธ์ฆ ๋ฐฉ์‹์ด ๋กœ๊ทธ์ธ/๋น„๋ฐ€๋ฒˆํ˜ธ, OAuth, 2๋‹จ๊ณ„ ์ธ์ฆ ๋“ฑ ์–ด๋–ค ๊ฒƒ์ด๋“ , ๊ฒฐ๊ตญ ํ† ํฐ์ด ๋ฐœ๊ธ‰๋ฉ๋‹ˆ๋‹ค. ์ด ํ† ํฐ์€ ์ดํ›„ ์š”์ฒญ์—์„œ ์‚ฌ์šฉ์ž ์‹๋ณ„์„ ์œ„ํ•ด ์ €์žฅ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด ํ† ํฐ์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด๊ณ  ์ด์ƒ์ ์ธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ† ํฐ์„ ์ˆ˜๋™์œผ๋กœ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ, ๋ณต์žกํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์„ ์ง€์›ํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ(์˜ˆ: Remix)๋ฅผ ์‚ฌ์šฉ ์ค‘์ด๋ผ๋ฉด, ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์ฟ ํ‚ค ์ธํ”„๋ผ๋ฅผ shared/api์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. Remix๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ๋Š” ํŠœํ† ๋ฆฌ์–ผ์˜ ์ธ์ฆ ์„น์…˜์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์—์„œ๋Š”, ํ† ํฐ์„ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ, ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ ๊ฐฑ์‹  ๋กœ์ง์„ ํ•จ๊ป˜ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ, ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ ๊ฐฑ์‹  ๋กœ์ง์„ ํ•จ๊ป˜ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. FSD์—์„œ๋Š” ํ† ํฐ์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Shared์— ์ €์žฅํ•˜๊ธฐโ€‹

shared/api์— ์ €์žฅํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์€ API ํด๋ผ์ด์–ธํŠธ์™€ ์ž˜ ๋งž์•„๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ์ธ์ฆ์ด ํ•„์š”ํ•œ ๋‹ค๋ฅธ ์š”์ฒญ ํ•จ์ˆ˜์—์„œ ์ด ํ† ํฐ์„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. API ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ˜์‘ํ˜• ์Šคํ† ์–ด๋‚˜ ๋ชจ๋“ˆ ์ˆ˜์ค€ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ํ† ํฐ์„ ์ €์žฅํ•˜๊ณ , login()/logout() ํ•จ์ˆ˜์—์„œ ํ•ด๋‹น ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ† ํฐ ์ž๋™ ๊ฐฑ์‹ ์€ API ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฏธ๋“ค์›จ์–ด ํ˜•ํƒœ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์š”์ฒญ๋งˆ๋‹ค ์‹คํ–‰๋˜๋ฉฐ, ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค:

  • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ฉด ์•ก์„ธ์Šค ํ† ํฐ๊ณผ ๊ฐฑ์‹  ํ† ํฐ์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ์ธ์ฆ์ด ํ•„์š”ํ•œ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์–ด ์š”์ฒญ์ด ์‹คํŒจํ•˜๋ฉด, ๊ฐฑ์‹  ํ† ํฐ์„ ์‚ฌ์šฉํ•ด ์ƒˆ๋กœ์šด ํ† ํฐ์„ ์š”์ฒญํ•˜๊ณ  ์ €์žฅํ•œ ํ›„, ์›๋ž˜ ์š”์ฒญ์„ ๋‹ค์‹œ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์˜ ๋‹จ์  ์ค‘ ํ•˜๋‚˜๋Š” ํ† ํฐ ๊ด€๋ฆฌ ๋กœ์ง์ด ์š”์ฒญ ๋กœ์ง๊ณผ ๊ฐ™์€ ์œ„์น˜์— ์žˆ์–ด, ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†๊ฒ ์ง€๋งŒ, ํ† ํฐ ๊ด€๋ฆฌ ๋กœ์ง์ด ๋ณต์žกํ•œ ๊ฒฝ์šฐ์—๋Š” ์š”์ฒญ๊ณผ ๊ด€๋ฆฌ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์š”์ฒญ ๋ฐ API ํด๋ผ์ด์–ธํŠธ๋Š” shared/api์— ๋‘๊ณ , ํ† ํฐ ๊ด€๋ฆฌ ๋กœ์ง์€ shared/auth์— ๋‘๋Š” ๋ฐฉ์‹์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ๋‹จ์ ์€ ๋ฐฑ์—”๋“œ๊ฐ€ ํ† ํฐ๊ณผ ํ•จ๊ป˜ ํ˜„์žฌ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ, ์ด ์ •๋ณด๋ฅผ ๋ณ„๋„๋กœ ์ €์žฅํ•˜๊ฑฐ๋‚˜ /me ๋˜๋Š” /users/current์™€ ๊ฐ™์€ ์—”๋“œํฌ์ธํŠธ์—์„œ ๋‹ค์‹œ ์š”์ฒญํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

Entities์— ์ €์žฅํ•˜๊ธฐโ€‹

FSD ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์‚ฌ์šฉ์ž ์—”ํ‹ฐํ‹ฐ ๋˜๋Š” ํ˜„์žฌ ์‚ฌ์šฉ์ž ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ๋‘ ์—”ํ‹ฐํ‹ฐ๋Š” ๊ฐ™์€ ๊ฒƒ์„ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

note

ํ˜„์žฌ ์‚ฌ์šฉ์ž๋Š” "viewer" ๋˜๋Š” "me"๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ถŒํ•œ๊ณผ ๊ฐœ์ธ ์ •๋ณด๋ฅผ ๊ฐ€์ง„ ๋‹จ์ผ ์ธ์ฆ ์‚ฌ์šฉ์ž์™€ ๊ณต๊ฐœ์ ์œผ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ •๋ณด๋กœ ๊ตฌ์„ฑ๋œ ๋ชจ๋“  ์‚ฌ์šฉ์ž ๋ชฉ๋ก์„ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

User ์—”ํ‹ฐํ‹ฐ์— ํ† ํฐ์„ ์ €์žฅํ•˜๋ ค๋ฉด model ์„ธ๊ทธ๋จผํŠธ์— ๋ฐ˜์‘ํ˜• ์Šคํ† ์–ด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šคํ† ์–ด๋Š” ํ† ํฐ๊ณผ ์‚ฌ์šฉ์ž ๊ฐ์ฒด๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

API ํด๋ผ์ด์–ธํŠธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ shared/api ์ •์˜๋˜๊ฑฐ๋‚˜ ์—”ํ‹ฐํ‹ฐ ์ „์ฒด์— ๋ถ„์‚ฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ฃผ์š” ๊ณผ์ œ๋Š” ๋ ˆ์ด์–ด์˜ ์ž„ํฌํŠธ ๊ทœ์น™(import rule on layers)์„ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ๋‹ค๋ฅธ ์š”์ฒญ์—์„œ๋„ ํ† ํฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ ˆ์ด์–ด ๊ทœ์น™: ์Šฌ๋ผ์ด์Šค์˜ ๋ชจ๋“ˆ์€ ์ž๊ธฐ๋ณด๋‹ค ๋‚ฎ์€ ๋ ˆ์ด์–ด์— ์œ„์น˜ํ•œ ๋‹ค๋ฅธ ์Šฌ๋ผ์ด์Šค๋งŒ ์ž„ํฌํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ์š”์ฒญ ์‹œ๋งˆ๋‹ค ํ† ํฐ ์ˆ˜๋™ ์ „๋‹ฌ
    ์ด ๋ฐฉ๋ฒ•์€ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ๋ฒˆ๊ฑฐ๋กญ๊ณ  ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ๋ณด์žฅ๋˜์ง€ ์•Š์œผ๋ฉด ์‹ค์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. ๋˜ํ•œ Shared์˜ API ํด๋ผ์ด์–ธํŠธ์— ๋ฏธ๋“ค์›จ์–ด ํŒจํ„ด์„ ์ ์šฉํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  2. ์•ฑ ์ „์—ญ์—์„œ ๊ธ€๋กœ๋ฒŒ ์Šคํ† ์–ด๋กœ ํ† ํฐ ๊ด€๋ฆฌ
    ํ† ํฐ์„ context๋‚˜ localStorage์— ์ €์žฅํ•˜๊ณ , shared/api์— ํ† ํฐ ์ ‘๊ทผ ํ‚ค๋ฅผ ๋ณด๊ด€ํ•ฉ๋‹ˆ๋‹ค. ํ† ํฐ์˜ ๋ฐ˜์‘ํ˜• ์ €์žฅ์†Œ๋Š” User ์—”ํ„ฐํ‹ฐ์—์„œ ๋‚ด๋ณด๋‚ด๋ฉฐ, ํ•„์š”ํ•œ ๊ฒฝ์šฐ context Provider๋Š” App ๋ ˆ์ด์–ด์—์„œ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ API ํด๋ผ์ด์–ธํŠธ ์„ค๊ณ„๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค์ง€๋งŒ, ์ƒ์œ„ ๋ ˆ์ด์–ด์— context ์ œ๊ณต์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์•”๋ฌต์ ์ธ ์˜์กด์„ฑ์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ context๋‚˜ localStorage๊ฐ€ ์ œ๋Œ€๋กœ ์„ค์ •๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ, ์œ ์šฉํ•œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  3. ํ† ํฐ ๋ณ€๊ฒฝ ์‹œ API ํด๋ผ์ด์–ธํŠธ ์—…๋ฐ์ดํŠธ
    ๋ฐ˜์‘ํ˜• ์Šคํ† ์–ด๋ฅผ ํ™œ์šฉํ•ด ์—”ํ‹ฐํ‹ฐ์˜ ์Šคํ† ์–ด๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค API ํด๋ผ์ด์–ธํŠธ์˜ ํ† ํฐ ์Šคํ† ์–ด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ตฌ๋…(subscribe)์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์ƒ์œ„ ๊ณ„์ธต์— ์•”๋ฌต์ ์ธ ์˜์กด์„ฑ์„ ๋งŒ๋“ ๋‹ค๋Š” ์ ์—์„œ๋Š” ์ด์ „ ํ•ด๊ฒฐ์ฑ…๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, ์ด ๋ฐฉ๋ฒ•์€ ๋” "๋ช…๋ นํ˜•(push)" ์ ‘๊ทผ์ด๊ณ , ์ด์ „ ๋ฐฉ๋ฒ•์€ ๋” "์„ ์–ธํ˜•(pull)" ์ ‘๊ทผ์ž…๋‹ˆ๋‹ค.

์—”ํ‹ฐํ‹ฐ์˜ model์— ํ† ํฐ์„ ์ €์žฅํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ฉด, ํ† ํฐ ๊ด€๋ฆฌ์™€ ๊ด€๋ จ๋œ ๋” ๋งŽ์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, model ์„ธ๊ทธ๋จผํŠธ์— ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ ๊ฐฑ์‹ ํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ํ† ํฐ์„ ๋ฌดํšจํ™”ํ•˜๋Š” ๋กœ์ง์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ์— ์š”์ฒญ์„ ๋ณด๋‚ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” User ์—”ํ‹ฐํ‹ฐ์˜ api ์„ธ๊ทธ๋จผํŠธ๋‚˜ shared/api๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Pages/Widgets์— ์ €์žฅํ•˜๊ธฐ (๊ถŒ์žฅํ•˜์ง€ ์•Š์Œ)โ€‹

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์—ญ์— ์ ์šฉ๋˜๋Š” ์ƒํƒœ(์˜ˆ: ์•ก์„ธ์Šค ํ† ํฐ)๋ฅผ ํŽ˜์ด์ง€๋‚˜ ์œ„์ ฏ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์˜ model ์„ธ๊ทธ๋จผํŠธ์— ํ† ํฐ ์Šคํ† ์–ด๋ฅผ ๋ฐฐ์น˜ํ•˜๋Š” ๋Œ€์‹ , ์ด ์•„ํ‹ฐํด์—์„œ ์ œ์‹œํ•œ ์ฒ˜์Œ ๋‘ ํ•ด๊ฒฐ์ฑ…์ธ Shared๋‚˜ Entities๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

๋กœ๊ทธ์•„์›ƒ ๋ฐ ํ† ํฐ ๋ฌดํšจํ™”โ€‹

๋กœ๊ทธ์•„์›ƒ ๊ธฐ๋Šฅ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, ์ด๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ ํŽ˜์ด์ง€๋Š” ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ๋ฐฑ์—”๋“œ์— ์ธ์ฆ๋œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ํ† ํฐ ์Šคํ† ์–ด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ž‘์—…์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ์š”์ฒญ์„ shared/api์— ๋ณด๊ด€ํ–ˆ๋‹ค๋ฉด, ๋กœ๊ทธ์ธ ํ•จ์ˆ˜ ๊ทผ์ฒ˜์— ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ๋‘๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์ด ์žˆ๋Š” ์œ„์น˜ ๊ทผ์ฒ˜์— ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋“  ํŽ˜์ด์ง€์— ๋‚˜ํƒ€๋‚˜๋Š” ํ—ค๋” ์œ„์ ฏ์— ๋กœ๊ทธ์•„์›ƒ ๋งํฌ๊ฐ€ ์žˆ๋‹ค๋ฉด, ํ•ด๋‹น ์š”์ฒญ์„ ๊ทธ ์œ„์ ฏ์˜ api ์„ธ๊ทธ๋จผํŠธ์— ๋ฐฐ์น˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

ํ† ํฐ ์Šคํ† ์–ด์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋Š” ๋กœ๊ทธ์•„์›ƒ ๋ฒ„ํŠผ์ด ์œ„์น˜ํ•œ ๊ณณ(์˜ˆ: ํ—ค๋” ์œ„์ ฏ)์—์„œ ํŠธ๋ฆฌ๊ฑฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์š”์ฒญ๊ณผ ์Šคํ† ์–ด ์—…๋ฐ์ดํŠธ๋ฅผ ํ•ด๋‹น ์œ„์ ฏ์˜ model ์„ธ๊ทธ๋จผํŠธ์—์„œ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž๋™ ๋กœ๊ทธ์•„์›ƒโ€‹

๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ ์‹คํŒจ๋‚˜ ๋กœ๊ทธ์ธ ํ† ํฐ ๊ฐฑ์‹  ์‹คํŒจ ์‹œ๋ฅผ ๋Œ€๋น„ํ•ด ์•ˆ์ „์žฅ์น˜๋ฅผ ๋งˆ๋ จํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ํ† ํฐ ์Šคํ† ์–ด๋ฅผ ๋น„์›Œ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ† ํฐ์„ Entities์— ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ, ์ด ๋กœ์ง์€ model ์„ธ๊ทธ๋จผํŠธ์— ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ† ํฐ์„ Shared์— ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ, ์ด ๋กœ์ง์„ shared/api์— ํฌํ•จํ•˜๋ฉด ์„ธ๊ทธ๋จผํŠธ๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ† ํฐ ๊ด€๋ฆฌ ๋กœ์ง์„ ๋ณ„๋„์˜ ์„ธ๊ทธ๋จผํŠธ(์˜ˆ: shared/auth)๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ๊ณ ๋ คํ•ด๋ณผ ๋งŒํ•ฉ๋‹ˆ๋‹ค.