๐Ÿ˜‚Hackathon ๋งˆ๊ฐ์ผ์„ ๋†“์ณค์ง€๋งŒ, ๋” ํฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์–ด์š”: ์˜ฌ๋ฐ”๋ฅธ Project Partner ์ฐพ๊ธฐ

๋ฐœํ–‰: (2025๋…„ 12์›” 23์ผ ์˜คํ›„ 04:52 GMT+9)
6 ๋ถ„ ์†Œ์š”
์›๋ฌธ: Dev.to

Source: Dev.to

์•„์ด๋””์–ด๋Š” ์žˆ์Šต๋‹ˆ๋‹ค.
์Šคํ‚ฌ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
๋™๊ธฐ๋ถ€์—ฌ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ๋„โ€ฆ ์—ฌ์ „ํžˆ ํ˜ผ์ž์„œ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์ฃ .

๊ทธ๋ž˜์„œ ์ €๋Š” Projura๋ฅผ ๋งŒ๋“ค๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ๊ฐœ๋ฐœ์ž, ๋””์ž์ด๋„ˆ, ๊ทธ๋ฆฌ๊ณ  ์ œ์ž‘์ž๋“ค์ด ๋ฌด์ž‘์œ„ ํŒ€์›์ด ์•„๋‹ˆ๋ผ ์ ํ•ฉํ•œ ํ”„๋กœ์ ํŠธ ํŒŒํŠธ๋„ˆ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ์•ฑ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๋งŒ๋“ค๋ฉด์„œ ์‹ค์ œ ํ˜‘์—… ์•ฑ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.

Projura๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”?

  • โœ… ํ”„๋กœํ•„ ๋งŒ๋“ค๊ธฐ
  • โœ… ๊ธฐ์ˆ  ์ถ”๊ฐ€
  • โœ… ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด ๊ฒŒ์‹œ
  • โœ… ๊ธฐ์ˆ  ๊ธฐ๋ฐ˜์œผ๋กœ ํ˜‘์—…์ž๋ฅผ ๋งค์นญ
  • โœ… ํ˜ผ์ž์„œ ๋งŒ๋“ค๊ธฐ๋ฅผ ๋ฉˆ์ถ”์„ธ์š”

๊ฐ€์งœ ๋„คํŠธ์›Œํ‚น์€ ์—†์Šต๋‹ˆ๋‹ค. ๋์—†๋Š” DM๋„ ์—†์Šต๋‹ˆ๋‹ค. ์˜ค์ง ๊ธฐ์ˆ  ๊ธฐ๋ฐ˜ ํ˜‘์—…๋งŒ.

ํ•ต์‹ฌ ์•„์ด๋””์–ด: ์ฝ”๋”ฉ์ฒ˜๋Ÿผ ์‚ฌ๋žŒ ๋งค์นญํ•˜๊ธฐ

Projura๋Š” ์‚ฌ๋žŒ์„ ๋ฌด์ž‘์œ„๋กœ ๋งค์นญํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

  • ์‚ฌ์šฉ์ž ๊ธฐ์ˆ 
  • ํ”„๋กœ์ ํŠธ ์š”๊ตฌ์‚ฌํ•ญ

์†Œํ”„ํŠธ์›จ์–ด๋Š” ๋ถ„์œ„๊ธฐ๋ฅผ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๊ณ  โ€” ๋…ผ๋ฆฌ๋ฅผ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

Step 1: ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ƒ์„ฑ (๋ฐฑ์—”๋“œ ๋กœ์ง)

ํ”„๋ก ํŠธ์—”๋“œ (JavaScript)

async function createUser() {
  const res = await fetch("http://127.0.0.1:5000/users", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      name: name.value,
      bio: bio.value,
      skills: skills.value
    })
  });

  const data = await res.json();
  userId = data.id;
}

๋ฐฑ์—”๋“œ (Flask API)

@app.route("/users", methods=["POST"])
def create_user():
    data = request.json
    user = User(
        name=data["name"],
        bio=data["bio"],
        skills=data["skills"]
    )
    db.session.add(user)
    db.session.commit()
    return jsonify({"id": user.id})

์ด๊ณณ์ด Projura๊ฐ€ ๊ณต์‹์ ์œผ๋กœ ๋‹น์‹ ์˜ ์กด์žฌ๋ฅผ ์ธ์‹ํ•˜๋Š” ์ˆœ๊ฐ„์ž…๋‹ˆ๋‹ค.

Step 2: ์‚ฌ์šฉ์ž ๋ชจ๋ธ (๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ๊ณณ)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    bio = db.Column(db.Text)
    skills = db.Column(db.Text)

์Šคํ‚ฌ์€ ์‰ผํ‘œโ€‘๊ตฌ๋ถ„ ๊ฐ’์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค, ์˜ˆ์‹œ:

"python, flask, frontend"

์ €์žฅํ•˜๊ธฐ ์‰ฝ๊ณ  ๋น„๊ตํ•˜๊ธฐ๋„ ์‰ฝ์Šต๋‹ˆ๋‹ค.

Step 3: ํ”„๋กœ์ ํŠธ๋Š” ์š”๊ตฌ ์‚ฌํ•ญ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค

class Project(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(150))
    description = db.Column(db.Text)
    required_skills = db.Column(db.Text)

์ด์ œ ํ”„๋กœ์ ํŠธ๋Š” ์‚ฌ์šฉ์ž์™€ ๋™์ผํ•œ ์–ธ์–ด์ธ ์Šคํ‚ฌ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Step 4: ๋งค์นญ ๋กœ์ง (์Šค๋งˆํŠธ ํŒŒํŠธ)

from difflib import SequenceMatcher

def skill_match(a, b):
    return SequenceMatcher(None, a, b).ratio()

์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž ์Šคํ‚ฌ๊ณผ ํ”„๋กœ์ ํŠธ ์Šคํ‚ฌ์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค; ์œ ์‚ฌ๋„ ๋น„์œจ์ด ๋†’์„์ˆ˜๋ก ๋” ์ข‹์€ ๋งค์น˜๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ˜‘์—…์„ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์˜์‚ฌ๊ฒฐ์ •์œผ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

5๋‹จ๊ณ„: ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๊ฐ€ ์ผ์น˜ํ•ด์•ผ ํ•จ

fetch(`${API}/projects/${userId}`)
  .then(res => res.json())
  .then(data => {
    projects.innerHTML = data.map(p =>
      `- ${p.title}
`
    ).join("");
  });

API๋Š” ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๊นจ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹จ๊ณ„ 6: ๋””๋ฒ„๊น… = ์‹ค์ œ ํ•™์Šต

Typical problems I hit:

  • โŒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด๋„ ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์Œ
  • โŒ API๋ฅผ ํ˜ธ์ถœํ–ˆ์ง€๋งŒ 500 ์˜ค๋ฅ˜ ๋ฐ˜ํ™˜
  • โŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ๋ถˆ์ผ์น˜
  • โŒ CORS๊ฐ€ ์š”์ฒญ์„ ์ฐจ๋‹จ

Each error forced me to learn:

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์Œ
  • ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๊ฐ€ ์ผ์น˜ํ•ด์•ผ ํ•˜๋Š” ์ด์œ 
  • ๋กœ๊ทธ๊ฐ€ ์ค‘์š”ํ•œ ์ด์œ 
  • โ€œ๋™์ž‘ํ•œ๋‹คโ€์™€ โ€œ์ž‘๋™ํ•œ๋‹คโ€๋Š” ๋‹ค๋ฆ„

This is where theory becomes engineering.

์™œ Projura๊ฐ€ ์ค‘์š”ํ•œ๊ฐ€

  • ์ดˆ๋ณด์ž๋“ค์€ ํŒ€์›์„ ์ฐพ๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช๋Š”๋‹ค
  • ์ˆ™๋ จ๋œ ์‚ฌ๋žŒ๋“ค์€ ์ข…์ข… ํ˜ผ์ž ์ž‘์—…ํ•œ๋‹ค
  • ํ›Œ๋ฅญํ•œ ์•„์ด๋””์–ด๊ฐ€ ์ผ์ฐ ์‚ฌ๋ผ์ง„๋‹ค

Projura๋Š” ์‚ฌ๋žŒ๋“ค์ด ์‹ค์ œ๋กœ ํ•จ๊ป˜ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์— ๊ธฐ๋ฐ˜ํ•ด ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€๋Šฅํ•œ ํ–ฅํ›„ ์—…๊ทธ๋ ˆ์ด๋“œ

  • ๐Ÿ”ฅ ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…
  • ๐Ÿ”ฅ GitHub ํ†ตํ•ฉ
  • ๐Ÿ”ฅ ์ถ”์ฒœ ์ ์ˆ˜ ๋งค๊ธฐ๊ธฐ
  • ๐Ÿ”ฅ ํ”„๋กœ์ ํŠธ ์ผ์ •
  • ๐Ÿ”ฅ ํŒ€ ๋Œ€์‹œ๋ณด๋“œ

๊ฐ ๊ธฐ๋Šฅ์€ Projura๋ฅผ ํ”„๋กœ๋•์…˜์— ๋” ๊ฐ€๊น๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์ตœ์ข… ์ƒ๊ฐ

Projura๋Š” ๋‚˜์—๊ฒŒ ์ค‘์š”ํ•œ ๊ฒƒ์„ ๊ฐ€๋ฅด์ณ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค: ์•ฑ์€ ์ฝ”๋“œ์— ๊ด€ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ธ๊ฐ„์˜ ๋ฌธ์ œ๋ฅผ ๋…ผ๋ฆฌ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์„ ์˜๋ฏธ ์žˆ๊ฒŒ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ๋‹จ์ˆœํžˆ ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์˜ํ–ฅ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

# coders language 
print("Happy building ๐Ÿš€๐Ÿ’ป ")
Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

Next.js 16 ์ถœ์‹œ๋จ: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ, ๋ณ€๊ฒฝ ์‚ฌํ•ญ ๋ฐ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์‹ฌ์ธต ๋ถ„์„

Next.js 16์ด ๊ณต์‹์ ์œผ๋กœ ์ถœ์‹œ๋˜์—ˆ์œผ๋ฉฐ ํ˜„๋Œ€ ์›น ๊ฐœ๋ฐœ ์ƒํƒœ๊ณ„์— ์ค‘์š”ํ•œ ํ˜์‹ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฒ„์ „์€ ์„ฑ๋Šฅ, ์บ์‹œ ๋ชจ๋ธ, ๋ผ์šฐํŒ… ๋™์ž‘โ€ฆ

๋ฐฑ์—”๋“œ API๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ๋งˆ์„ธ์š”: TypeScriptโ€‘First Mock Server์ธ Fakelab ์†Œ๊ฐœ

Fakelab โ€“ TypeScript ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ์œ„ํ•œ Realistic Mock API Server ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์ด๋Ÿฐ ์ƒํ™ฉ์„ ๊ฒช์–ด๋ดค์„ ๊ฒ๋‹ˆ๋‹ค: ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ์ค€๋น„ํ–ˆ์ง€๋งŒ, ๋ฐฑ์—”๋“œโ€ฆ

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ 5๊ฐœ์›” ์ฐจ์— ๋น„์ „ํ†ต์ ์ธ ์›€์ง์ž„์„ ์‹œ๋„ํ•˜๊ธฐ

๋น„์ „ํ†ต์ ์ธ ๊ฒฐ์ •: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•œ ์ง€ 5๊ฐœ์›” ๋œ ์‚ฌ๋žŒ์—๊ฒŒ๋Š” ์ง๊ด€์— ๋ฐ˜ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ๋Š” ์›€์ง์ž„์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค: ์ €๋Š” ์ œ f๋ฅผ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ๋Œ์•„๊ฐ€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Dev7 ์ผ๊ธฐ: ์™œ ๋‚ด๊ฐ€ ์ง์ ‘ design system์„ ๋งŒ๋“ค๊ธฐ๋กœ ํ–ˆ๋Š”๊ฐ€?

์™œ ๋‚ด๊ฐ€ ๋‚ด ๋””์ž์ธ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค๊ธฐ๋กœ ํ–ˆ๋Š”๊ฐ€? ์ด๊ฒƒ์€ ์•„๋งˆ๋„ ์˜ฌํ•ด ๋งˆ์ง€๋ง‰ ํฌ์ŠคํŒ…์ด ๋  ๊ฒƒ์ด๋‹ค. ํ”„๋กœ์ ํŠธ ํšŒ๊ณ ๋ฅผ ํ•˜๋Š” ๋Œ€์‹ , ๋‚˜๋Š” ํ™œ์šฉํ•˜๊ณ  ์‹ถ๋‹ค...