Skip to content

100-hours-a-week/3-logan-cho-community-FE

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

21 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Kaboocam ์ปค๋ฎค๋‹ˆํ‹ฐ ํ”„๋กœ์ ํŠธ

ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

๊ฐœ์ธ์ ์ธ ๊ณ ๋ฏผ๊ณผ ๊ฐœ๋ฐœ์„ ์ฃผ์ œ๋กœ ์„œ๋กœ ์†Œํ†ตํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. Vanilla JavaScript๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ–ˆ์œผ๋ฉฐ, ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ์ง์ ‘ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ์ธ์› ๋ฐ ๊ธฐ๊ฐ„

  • ๊ฐœ๋ฐœ๊ธฐ๊ฐ„: 2025-10-01 ~ 2025-12-08
  • ๊ฐœ๋ฐœ ์ธ์›: ํ”„๋ก ํŠธ์—”๋“œ/๋ฐฑ์—”๋“œ 1๋ช… (๋ณธ์ธ)

์‚ฌ์šฉ ๊ธฐ์ˆ  ๋ฐ ๋„๊ตฌ

Front-end

  • ์–ธ์–ด: Vanilla JavaScript (ES6+)
  • ์Šคํƒ€์ผ๋ง: CSS3, Bootstrap 5.3.8
  • ์„œ๋ฒ„: Express.js 5.1.0 (์ •์  ํŒŒ์ผ ์„œ๋น™)
  • ํ…Œ์ŠคํŠธ: Jest 30.2.0, Supertest 7.1.4

Back-end

์ธํ”„๋ผ & ๋ฐฐํฌ

  • (์ฒจ๋ถ€ ์˜ˆ์ •)

์ฃผ์š” ๊ธฐ๋Šฅ

1. ์ธ์ฆ ์‹œ์Šคํ…œ

  • JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ (Access Token + HttpOnly Refresh Token)
  • ์ž๋™ ํ† ํฐ ๊ฐฑ์‹  (Refresh Token ํ™œ์šฉ)
  • ์ด๋ฉ”์ผ ์ธ์ฆ ์ฝ”๋“œ ๋ฐœ์†ก ๋ฐ ๊ฒ€์ฆ
  • ํšŒ์› ํƒˆํ‡ด ๋ฐ ๋ณต๊ตฌ ๊ธฐ๋Šฅ

2. ๊ฒŒ์‹œ๊ธ€ ๊ด€๋ฆฌ

  • ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ, ์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œ (CRUD)
  • ์ปค์„œ ๊ธฐ๋ฐ˜ ๋ฌดํ•œ ์Šคํฌ๋กค ํŽ˜์ด์ง€๋„ค์ด์…˜
  • ์ตœ์‹ ์ˆœ/์ธ๊ธฐ์ˆœ ์ •๋ ฌ
  • ๊ฒŒ์‹œ๊ธ€ ์ข‹์•„์š” ๊ธฐ๋Šฅ
  • ์กฐํšŒ์ˆ˜ ์นด์šดํŒ…

3. ๋Œ“๊ธ€ ์‹œ์Šคํ…œ

  • ๋Œ“๊ธ€ ์ž‘์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ
  • ์ปค์„œ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋„ค์ด์…˜
  • ์ž‘์„ฑ์ž ํ‘œ์‹œ ๋ฐ ๋ณธ์ธ ๋Œ“๊ธ€ ๊ตฌ๋ถ„

4. ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ

  • ์—…๋กœ๋“œ: AWS S3 Presigned URL ํ™œ์šฉ
  • ์กฐํšŒ: CloudFront CDN + Signed Cookie ์ธ์ฆ
  • ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ ๋ฐ ๊ฒŒ์‹œ๊ธ€ ์ด๋ฏธ์ง€ ์ง€์›

5. ๋งˆ์ดํŽ˜์ด์ง€

  • ํ”„๋กœํ•„ ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ
  • ๋‹‰๋„ค์ž„ ๋ณ€๊ฒฝ
  • ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ
  • ํšŒ์› ํƒˆํ‡ด
ํด๋” ๊ตฌ์กฐ
โ”œโ”€โ”€ .dockerignore
โ”œโ”€โ”€ .github/
โ”œโ”€โ”€ .gitignore
โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ jest.config.js
โ”œโ”€โ”€ jsconfig.json
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ server.js                    # Express ์ •์  ํŒŒ์ผ ์„œ๋ฒ„
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ server.test.js          # ์„œ๋ฒ„ ํ…Œ์ŠคํŠธ
โ””โ”€โ”€ public/
    โ”œโ”€โ”€ index.html              # ๋ฉ”์ธ ํŽ˜์ด์ง€
    โ”œโ”€โ”€ css/
    โ”‚   โ”œโ”€โ”€ reset.css          # CSS ๋ฆฌ์…‹
    โ”‚   โ”œโ”€โ”€ theme.css          # ํ…Œ๋งˆ ๋ณ€์ˆ˜
    โ”‚   โ””โ”€โ”€ layout.css         # ๋ ˆ์ด์•„์›ƒ ์Šคํƒ€์ผ
    โ”œโ”€โ”€ js/
    โ”‚   โ”œโ”€โ”€ config.js          # API ํ™˜๊ฒฝ ์„ค์ •
    โ”‚   โ”œโ”€โ”€ api.js             # API ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ
    โ”‚   โ”œโ”€โ”€ cdn.js             # CDN ์ด๋ฏธ์ง€ ๋กœ๋” (Signed Cookie)
    โ”‚   โ”œโ”€โ”€ dom.js             # DOM ์œ ํ‹ธ๋ฆฌํ‹ฐ
    โ”‚   โ”œโ”€โ”€ common.js          # ๊ณตํ†ต ์œ ํ‹ธ๋ฆฌํ‹ฐ
    โ”‚   โ””โ”€โ”€ storage.js         # ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ๊ด€๋ฆฌ
    โ”œโ”€โ”€ components/
    โ”‚   โ”œโ”€โ”€ navbar.html        # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”
    โ”‚   โ”œโ”€โ”€ navbar.css
    โ”‚   โ”œโ”€โ”€ post-card.css      # ๊ฒŒ์‹œ๊ธ€ ์นด๋“œ ์Šคํƒ€์ผ
    โ”‚   โ””โ”€โ”€ mypage-dropdown.css
    โ””โ”€โ”€ pages/
        โ”œโ”€โ”€ home/              # ํ™ˆ ํŽ˜์ด์ง€
        โ”‚   โ”œโ”€โ”€ index.html
        โ”‚   โ”œโ”€โ”€ home.html
        โ”‚   โ”œโ”€โ”€ home.js
        โ”‚   โ””โ”€โ”€ home.css
        โ”œโ”€โ”€ auth/              # ์ธ์ฆ ๊ด€๋ จ ํŽ˜์ด์ง€
        โ”‚   โ”œโ”€โ”€ login/
        โ”‚   โ”‚   โ”œโ”€โ”€ login.html
        โ”‚   โ”‚   โ”œโ”€โ”€ login.js
        โ”‚   โ”‚   โ””โ”€โ”€ login.css
        โ”‚   โ”œโ”€โ”€ signup/
        โ”‚   โ”‚   โ”œโ”€โ”€ signup.html
        โ”‚   โ”‚   โ”œโ”€โ”€ signup.js
        โ”‚   โ”‚   โ””โ”€โ”€ signup.css
        โ”‚   โ””โ”€โ”€ recover/
        โ”‚       โ”œโ”€โ”€ recover.html
        โ”‚       โ””โ”€โ”€ recover.js
        โ”œโ”€โ”€ board/             # ๊ฒŒ์‹œํŒ ๊ด€๋ จ ํŽ˜์ด์ง€
        โ”‚   โ”œโ”€โ”€ board.html     # ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก
        โ”‚   โ”œโ”€โ”€ board.js
        โ”‚   โ”œโ”€โ”€ board.css
        โ”‚   โ”œโ”€โ”€ postDetail.html # ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ
        โ”‚   โ”œโ”€โ”€ postDetail.js
        โ”‚   โ”œโ”€โ”€ postDetail.css
        โ”‚   โ””โ”€โ”€ comment.js     # ๋Œ“๊ธ€ ๊ด€๋ฆฌ
        โ””โ”€โ”€ mypage/            # ๋งˆ์ดํŽ˜์ด์ง€
            โ”œโ”€โ”€ mypage.html
            โ”œโ”€โ”€ mypage.js
            โ””โ”€โ”€ mypage.css

๊ตฌ์กฐ:

  • api.js: ๋ชจ๋“  API ์š”์ฒญ ์ค‘์•™ ๊ด€๋ฆฌ
  • cdn.js: CloudFront ์ด๋ฏธ์ง€ ๋กœ๋”ฉ
  • dom.js: DOM ์กฐ์ž‘ ์œ ํ‹ธ๋ฆฌํ‹ฐ
  • storage.js: ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ๊ด€๋ฆฌ
  • common.js: ๊ณตํ†ต ํ•จ์ˆ˜ (๋‚ ์งœ ํฌ๋งทํŒ… ๋“ฑ)

์„œ๋น„์Šค ํ™”๋ฉด

ํ™ˆ

ํ™ˆ ํ™”๋ฉด

์ธ์ฆ

๋กœ๊ทธ์ธ ํšŒ์›๊ฐ€์ž… ์ด๋ฉ”์ผ
๋กœ๊ทธ์ธ ํšŒ์›๊ฐ€์ž…

๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ

๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ
๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ

๊ฒŒ์‹œ๊ธ€ ํŽธ์ง‘

๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ •
๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ •

ํšŒ์› ๋ณต๊ตฌ

๋ณต๊ตฌ ํŒ์—… ํšŒ์› ๋ณต๊ตฌ
๋งˆ์ดํŽ˜์ด์ง€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ

ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

1. CORS ๋ฌธ์ œ ํ•ด๊ฒฐ

๋ฌธ์ œ: ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋ฐฑ์—”๋“œ API ํ˜ธ์ถœ ์‹œ CORS ์—๋Ÿฌ ๋ฐœ์ƒ

ํ•ด๊ฒฐ:

  • ๋ฐฑ์—”๋“œ์—์„œ CORS ์„ค์ • ์ถ”๊ฐ€
  • credentials: "include" ์˜ต์…˜์œผ๋กœ ์ฟ ํ‚ค ์ „์†ก ํ—ˆ์šฉ
  • CloudFront์—์„œ๋„ CORS ํ—ค๋” ์„ค์ •

2. Signed Cookie ์ธ์ฆ ๋ฌธ์ œ

๋ฌธ์ œ: CloudFront์—์„œ ์ด๋ฏธ์ง€ ๋กœ๋“œ ์‹œ 403 ์—๋Ÿฌ ๋ฐ˜๋ณต ๋ฐœ์ƒ

ํ•ด๊ฒฐ:

  • fetch API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ credentials: "include" ์„ค์ •
  • <img> ํƒœ๊ทธ์˜ src๋ฅผ ์ง์ ‘ ์„ค์ •ํ•˜๋Š” ๋Œ€์‹  Blob URL ์‚ฌ์šฉ
  • ์ตœ์ดˆ ์š”์ฒญ ์‹คํŒจ ์‹œ ์ž๋™์œผ๋กœ Signed Cookie ๋ฐœ๊ธ‰ ๋ฐ ์žฌ์‹œ๋„ ๋กœ์ง ๊ตฌํ˜„
// ์ด๋ฏธ์ง€ ์กฐํšŒ ์‹œ Signed Cookie ์ž๋™ ์ฒ˜๋ฆฌ
async fetchImage(cdnBaseUrl, objectKey) {
  let res = await fetch(url, { credentials: "include" });

  // 401/403 ์‹œ ์ฟ ํ‚ค ๋ฐœ๊ธ‰ ํ›„ ์žฌ์‹œ๋„
  if ((res.status === 401 || res.status === 403) && !this._cookieReady) {
    await this._ensureCookieOnce();
    res = await fetch(url, { credentials: "include" });
  }

  return await res.blob();
}

3. ๋ฌดํ•œ ์Šคํฌ๋กค ์ค‘๋ณต ๋กœ๋”ฉ ๋ฐฉ์ง€

๋ฌธ์ œ: ์Šคํฌ๋กค ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์ค‘๋ณต API ํ˜ธ์ถœ

ํ•ด๊ฒฐ:

  • isLoading ํ”Œ๋ž˜๊ทธ๋กœ ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€
  • Intersection Observer API ํ™œ์šฉ ๊ณ ๋ ค
let isLoading = false;

async function loadMorePosts() {
  if (isLoading || !hasNext) return;

  isLoading = true;
  try {
    const data = await api.getPosts({ cursor: nextCursor });
    // ... ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
  } finally {
    isLoading = false;
  }
}

ํ”„๋กœ์ ํŠธ ํ›„๊ธฐ

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

๊ฐœ์„  ๊ณ„ํš

  • ๊ฒŒ์‹œ๋ฌผ ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€์—์„œ ๊ฒŒ์‹œ๋ฌผ ์š”์•ฝ์„ ๋ณด์—ฌ์ฃผ๋„๋ก ์ˆ˜์ •

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

โšก