λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ€” 회고: Retrospect

[SOPT] 30κΈ° 솝컀톀 ν›„κΈ°

by 🐀 쀀콩이 2022. 12. 31.

🐷 μΈνŠΈλ‘œ

 

SOPT νšŒμ›μ΄λΌλ©΄ λˆ„κ΅¬λ‚˜ μ°Έμ—¬ν•˜κ³  싢은 솝컀톀!

 

λ°€μƒˆ 솝컀톀을 마치고 집에 λŒμ•„μ™€ μ΅œλŒ€ν•œ μƒμƒν•œ κΈ°μ–΅μœΌλ‘œ ν›„κΈ°λ₯Ό μž‘μ„±ν•΄λ³΄λ € ν•œλ‹€.

μ½”λ‘œλ‚˜ 거리두기 μ œν•œμ΄ ν•΄μ œλ¨μ— 따라 이번 해컀톀은 전체 μ˜€ν”„λΌμΈμœΌλ‘œ μ§„ν–‰λ˜μ—ˆλ‹€.

인원 수 μ œν•œμ΄ μžˆμ–΄μ„œ 파트 별 μ„ μ°©μˆœμœΌλ‘œ 신청을 λ°›μ•˜λŠ”λ° λ‹€ν–‰νžˆλ„ 신청에 μ„±κ³΅ν–ˆλ‹€.

 

λ‹€μ–‘ν•œ 팁 μ£Όμ‹  OB λΆ„λ“€ κ°μ‚¬ν•©λ‹ˆλ‹€.. 🐷

 

 

πŸ‘» μ£Όμ œ

 

 

이번 30κΈ° 솝컀톀은 μ£Όμ œλŠ” μΌμƒμ΄μ—ˆλ‹€.

 

μ›Œλ‚™ 포괄적인 주제라 λ‹€μ–‘ν•œ 아이디어듀이 λ‚˜μ˜€κ² λ‹€λŠ” 생각을 ν–ˆκ³  우리 νŒ€μ€ κ³§λ°”λ‘œ μ•„μ΄λ°μ΄μ…˜μ— λ“€μ–΄κ°”λ‹€.

 

우리 νŒ€μ€ ν˜‘μ—… 툴인 Notion을 ν™œμš©ν•΄μ„œ μ•„μ΄λ°μ΄μ…˜μ„ μ§„ν–‰ν–ˆλŠ”λ°, λΈŒλ ˆμΈμŠ€ν† λ°μ„ ν•˜λ“―μ΄ 자유둭게 아이디어λ₯Ό μ μ–΄λ‚˜κ°€λ©° λ°œμ‚°ν•˜λŠ” 과정을 거치고, 아이디어λ₯Ό μΆ”λ¦¬λŠ” 수렴, μ΅œμ’…μ μΈ 아이디어 ν™•μ •, 디벨둭 μˆœμ„œλ‘œ μ§„ν–‰ν–ˆλ‹€.

 

λ‹€μ–‘ν•œ μ˜κ²¬λ“€ 쀑 우리 νŒ€μ€ μΌμƒ 속 고맙고 λ―Έμ•ˆν•œ λ§ˆμŒμ„ 자유둭게 기둝할 수 μžˆλŠ” μ„œλΉ„μŠ€λ₯Ό κ΅¬μƒν•˜κ²Œ λ˜μ—ˆλ‹€.

 

 

🫢🏼 μ„œλΉ„μŠ€ μ†Œκ°œ

 

 

과일을 λ‚˜νƒ€λ‚΄λŠ” 단어인 fruit을 μ΄μš©ν•΄ 'ν‘Έλ¦‡ν‘Έλ¦‡ν•œ λ‚˜λ¬΄λ₯Ό μ—΄λ§€λ‘œ κΎΈλ―ΈλŠ” μ„œλΉ„μŠ€' ν‘Έλ¦‡ν‘Έλ¦‡ μ„œλΉ„μŠ€λ‹€.

 

μ„œλΉ„μŠ€μ˜ 핡심 κΈ°λŠ₯ 4κ°€μ§€λŠ” 열맀λ₯Ό 맀달고, 맀단 열맀λ₯Ό μˆ˜ν™•ν•˜κ³ , 읡λͺ…μ˜ 열맀듀을 톡해 νžλ§ν•˜κ³ , κ³΅κ°λ˜λŠ” μ—΄λ§€μ˜ λ‚˜λ¬΄μ— 물을 μ£ΌλŠ” 것이닀. 푸릇푸릇 μ„œλΉ„μŠ€μ—μ„œ μ—΄λ§€λŠ” κ²Œμ‹œκΈ€μ„ μ˜λ―Έν•˜κ³  κ°μ‚¬ν•˜λŠ” 글은 감 열맀가 맀달리고 μ‚¬κ³Όν•˜λŠ” 글은 사과 열맀가 맀달린닀. 물을 μ£ΌλŠ” 것은 글에 곡감 ν‘œμ‹œλ₯Ό ν•œλ‹€λŠ” μ˜λ―Έμ΄λ‹€.

 

 

πŸ€– μ„œλ²„ 파트 μž‘μ—…

 

아이디어가 ν™•μ •λœ ν›„ 기획 파트 νŒ€μ›λ“€μ΄ ꡬ체적인 μ™€μ΄μ–΄ν”„λ ˆμž„ μž‘μ—…μ— λ“€μ–΄κ°”κ³  λ””μžμ΄λ„ˆ, κ°œλ°œμžμ™€ μ†Œν†΅μ„ 톡해 μ„œλΉ„μŠ€μ˜ flowλ₯Ό ν™•μ •μ§€μ—ˆλ‹€. λ‚˜λ₯Ό ν¬ν•¨ν•œ μ„œλ²„νŒŒνŠΈλŠ” κ³§λ°”λ‘œ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μœ„ν•œ μ»¬λ ‰μ…˜ 섀계에 λ“€μ–΄κ°”λ‹€. 무박 2일 16μ‹œκ°„ λ™μ•ˆ μ§„ν–‰λ˜λŠ” 짧은 κΈ°κ°„μ˜ ν•΄μ»€ν†€μ΄μ—ˆκΈ° λ•Œλ¬Έμ— κΈ°λŠ₯이 κ°„μ†Œν•΄μ„œ μ»¬λ ‰μ…˜μ€ 두 κ°€μ§€λ‘œ μΆ©λΆ„ν–ˆλ‹€.

 

User Collection

import mongoose from "mongoose";
import { UserInfo } from "../interfaces/user/UserInfo";

const UserSchema = new mongoose.Schema({
    userNickname: {
        type: String,
        required: true
    },
    userProfileImageUrl: {
        type: String,
        required: true
    }
});

export default mongoose.model<UserInfo & mongoose.Document>("User", UserSchema);

 

Fruit Collection

import mongoose, { Mongoose } from "mongoose";
import { FruitInfo } from "../interfaces/fruit/FruitInfo";

const FruitSchema = new mongoose.Schema({
    type: {
        type: Number,
        required: true
    },
    contents: {
        type: String,
        required: true
    },
    wateringCount: {
        type: Number,
        required: true
    },
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        required: true,
        ref: "User"
    },
    onTree: {
        type: Boolean,
        required: true
    }
},
{
    timestamps: true
});

export default mongoose.model<FruitInfo & mongoose.Document>("Fruit", FruitSchema);

 

μ»¬λ ‰μ…˜ 섀계λ₯Ό 마치고 ν•„μš”ν•œ API듀을 νŒŒμ•…ν•˜λ©° ν΄λΌμ΄μ–ΈνŠΈ 파트 νŒ€μ›λ“€μ˜ μ›ν™œν•œ μž‘μ—…μ„ μœ„ν•΄ API λͺ…μ„Έμ„œλ₯Ό μž‘μ„±ν•˜κΈ° μ‹œμž‘ν–ˆλ‹€. λ…Έμ…˜μ„ μ΄μš©ν•΄μ„œ μž‘μ„±ν•˜μ˜€κ³  λͺ…μ„Έμ„œλ₯Ό λͺ¨λ‘ μž‘μ„±ν•œ ν›„μ—λŠ” ν΄λΌμ΄μ–ΈνŠΈ νŒ€μ›κ³Ό 회의λ₯Ό 톡해 μ΄λŒ€λ‘œ 진행해도 쒋을지 확인을 λ§ˆμ³€λ‹€.

 

 

API λͺ…μ„Έμ„œλŠ” Notion을 ν™œμš©ν•΄μ„œ μž‘μ„±ν–ˆκ³  end point 에 μž‘μ„±ν•œ λ‚΄λΆ€ νŽ˜μ΄μ§€μ— λ“€μ–΄κ°€λ©΄ 각 API λ³„λ‘œ ν•„μš”ν•œ Request Header, Request Param, Request Query, Request Body, Response Body, Response Code 등을 λͺ¨λ‘ μ •λ¦¬ν•΄λ‘μ—ˆλ‹€.

 

API λͺ…μ„Έμ„œ μž‘μ„±μ„ 마치고 본격적인 코딩에 λ“€μ–΄κ°€κΈ° μ „, μ„œλ²„ 파트 νŒ€μ›κ³Ό 각자 λ‹΄λ‹Ήν•  API, μ½”λ“œ μ»¨λ²€μ…˜, 컀밋 λ©”μ‹œμ§€ μ»¨λ²€μ…˜, Git 브랜치 μ „λž΅μ— λŒ€ν•œ 회의λ₯Ό μ§„ν–‰ν–ˆλ‹€. ν˜‘μ—… 방식을 λͺ¨λ‘ μ •ν•˜κ³  λ‚˜μ„œμ•Ό λ§ˆμΉ¨λ‚΄ 코딩을 μ‹œμž‘ν–ˆλ‹€.

 

 

πŸ”« μ—λŸ¬ λ°œμƒκ³Ό ν•΄κ²°

 

μž‘μ—… 총 μ†Œμš” μ‹œκ°„μ€ μ˜€ν›„ 8μ‹œλΆ€ν„° μ˜€μ „ 6μ‹œ 30λΆ„κΉŒμ§€ 10μ‹œκ°„ 30λΆ„ μ •λ„μ˜€λ‹€. 이 κ³Όμ •μ—μ„œ 골치 μ•„ν”ˆ μ—λŸ¬κ°€ 두 번 정도 λ°œμƒν–ˆλ‹€. 첫 번째 μ—λŸ¬λ‘œ μ„œλ²„ 파트 νŒ€μ›κ³Ό 거의 1μ‹œκ°„μ„ λ„˜κ²Œ λ‚­λΉ„ν–ˆλ‹€.

 

GET /fruit/my API μš”μ²­μ„ 보내면 λ°œμƒν•˜λŠ” λ¬Έμ œμ˜€λŠ”λ° μ•„λž˜μ™€ 같은 μ—λŸ¬ λ©”μ‹œμ§€κ°€ λ‚˜μ™”λ‹€.

 

CastError: Cast to ObjectId failed for value "my" (type string) at path "_id" for model "Fruit"
    at model.Query.exec (/Users/orijoon98/Desktop/Workspace/Nangman-Server/node_modules/mongoose/lib/query.js:4719:21)
    at model.Query.Query.then (/Users/orijoon98/Desktop/Workspace/Nangman-Server/node_modules/mongoose/lib/query.js:4818:15)

 

ꡬ글링도 많이 해보고 κ΄€λ ¨λœ μ½”λ“œλ₯Ό μ „μ²΄μ μœΌλ‘œ μ‚΄ν”Όλ©° λ¬Έμ œμ μ„ 찾아보렀 ν–ˆμ§€λ§Œ μ‹€νŒ¨ν–ˆκ³  κ²°κ΅­ μ„œλ²„ 파트μž₯λ‹˜μ—κ²Œ 도움을 μš”μ²­ν–ˆλ‹€. μ„œλ²„ 파트μž₯λ‹˜μ˜ λ„μ›€μœΌλ‘œ 원인을 μ•Œκ²Œ λ˜μ—ˆλŠ”λ° λΌμš°ν„°κ°€ μ›μΈμ΄λΌλŠ” 것을 μ•Œκ²Œ λ˜μ—ˆλ‹€.

 

μ—λŸ¬κ°€ λ‚¬λ˜ λΌμš°ν„° μ½”λ“œ

import { Router } from "express";
import { body } from "express-validator/check";
import { FruitController } from "../controllers";

const router: Router = Router();

router.get("/", FruitController.getFruits);
router.post(
    "/",
    [body("type").notEmpty(), body("contents").notEmpty()],
    FruitController.createFruit
);
router.get("/my/tree", FruitController.getMyFruitsOnTree);
router.get("/:fruitId", FruitController.findFruitById);
router.get("/my", FruitController.getMyFruits);
router.put("/:fruitId/water", FruitController.putWateringCount);

export default router;

 

μ—¬κΈ°μ„œ router.get("/my", FruitController.getMyFruits); 이 λΆ€λΆ„μœΌλ‘œ μš”μ²­μ΄ λ„˜μ–΄κ°€κΈ° 전에 λΌμš°ν„°λŠ” μœ„μ—μ„œ μ•„λž˜λ‘œ ν™•μΈν•˜λ©° λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ— /fruit/my 둜 보낸 μš”μ²­μ΄ router.get("/:fruitId", FruitController.findFruitById); λΌμš°ν„°λ‘œ λΌμš°νŒ… λ˜μ–΄ fruitId Param에 myκ°€ λ“€μ–΄κ°€μ„œ λ°œμƒν•œ μ—λŸ¬μ˜€λ‹€.

 

ν•΄κ²°ν•œ μ½”λ“œ

import { Router } from "express";
import { body } from "express-validator/check";
import { FruitController } from "../controllers";

const router: Router = Router();

router.get("/", FruitController.getFruits);
router.post(
    "/",
    [body("type").notEmpty(), body("contents").notEmpty()],
    FruitController.createFruit
);
router.get("/my/tree", FruitController.getMyFruitsOnTree);
router.get("/my", FruitController.getMyFruits);
router.get("/:fruitId", FruitController.findFruitById);
router.put("/:fruitId/water", FruitController.putWateringCount);

export default router;

 

두 λΌμš°ν„°μ˜ μˆœμ„œλ₯Ό λ°”κΏ”μ£ΌλŠ” 것 만으둜 μ—λŸ¬λ₯Ό ν•΄κ²°ν•  수 μžˆμ—ˆλ‹€. λΌμš°ν„°μ˜ λ™μž‘ 방식을 μ œλŒ€λ‘œ μ΄ν•΄ν•˜κ³  μžˆμ§€ μ•Šμ•„μ„œ 이런 문제λ₯Ό νŒŒμ•…ν•˜μ§€ λͺ»ν–ˆμ—ˆλ‹€λŠ” 생각이 λ“€μ—ˆκ³  κΈ°μˆ μ— λŒ€ν•œ μ΄ν•΄λŠ” μ—­μ‹œ μ€‘μš”ν•˜λ‹€λŠ” κ±Έ λ‹€μ‹œ ν•œλ²ˆ λ˜μƒˆκΈ°κ²Œ λ˜μ—ˆλ‹€.

 

두 번째 λ¬Έμ œλŠ” μ‚¬μ†Œν•œ μ‹€μˆ˜λ‘œ λ°œμƒν•œ μ—λŸ¬μ˜€λ‹€. μž‘μ—…μ„ λͺ¨λ‘ 마친 ν›„ AWS EC2 에 λ°°ν¬ν•˜μ—¬ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν–ˆλŠ”λ° 둜컬 ν˜ΈμŠ€νŠΈμ—μ„  μ •μƒμ μœΌλ‘œ μž‘λ™ν•˜λ˜ API듀이 μž‘λ™ν•˜μ§€ μ•Šμ•˜λ‹€. npm, yarn λ“±μ˜ 버전을 둜컬과 비ꡐ해보고 λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 확인해보고 λΉŒλ“œλ„ λ‹€μ‹œ ν•΄λ³΄λŠ” λ“± λ‹€μ–‘ν•œ μ‹œλ„λ₯Ό ν•΄λ³΄μ•˜λŠ”λ° λ¬Έμ œλŠ” ν•΄κ²°λ˜μ§€ μ•Šμ•˜λ‹€. μ£Όλ³€ μ‚¬λžŒλ“€μ—κ²Œ 도움을 μš”μ²­ν–ˆμ§€λ§Œ λͺ¨λ‘λ“€ 원인을 μ°Ύμ§€λŠ” λͺ»ν–ˆλ‹€. λ§μ—°μžμ‹€ν•˜λ˜ 와쀑 AWS EC2에 μž‘μ„±ν–ˆλ˜ .env νŒŒμΌμ— λ°μ΄ν„°λ² μ΄μŠ€ μ ‘κ·Ό url을 λ‹€λ₯Έ 값을 λ³΅μ‚¬ν–ˆλ‹€λŠ” 것을 λ°œκ²¬ν•˜κ²Œ λ˜μ—ˆλ‹€. λ„ˆλ¬΄λ‚˜ μ‚¬μ†Œν•œ μ‹€μˆ˜μ˜€μ§€λ§Œ κ·Έ μ‹€μˆ˜ ν•˜λ‚˜λ‘œ 1μ‹œκ°„μ„ λ„˜κ²Œ 애먹은 κ±Έ μƒκ°ν•˜λ©΄ ...πŸ˜₯ 정신차리고 μ½”λ”©ν•΄μ•Όκ² λ‹€.

 

 

πŸ€” μ†Œκ°

 

무박 2일 λ™μ•ˆ μ§„ν–‰λ˜λŠ” 해컀톀은 이번이 μ„Έ 번째 κ²½ν—˜μ΄μ—ˆλ‹€. μ§€λ‚œ 두 번의 해컀톀 λ•Œ, λ°€ μƒˆ νŒ€μ›λ“€κ³Ό ν•¨κ»˜ κ³ μƒν•˜λ©° μ„œλΉ„μŠ€λ₯Ό λ§Œλ“€μ–΄κ°€λŠ” 과정이 μ¦κ±°μ›Œμ„œ 이번 해컀톀도 μ‹ μ²­ν•˜κ²Œ λ˜μ—ˆλŠ”λ° μ—­μ‹œλ‚˜ 즐거웠닀. SOPT 동아리 λ‚΄μ—μ„œ μ§„ν–‰ν•œ ν•΄μ»€ν†€μ΄μ—ˆκΈ°μ— μΉœν•œ μ‚¬λžŒλ“€λ„ λ§Žμ•„μ„œ λ”μš± 더 즐겁게 행사λ₯Ό 즐긴 것 κ°™λ‹€.

 

λ””μžμΈ 파트, μ›Ή, μ•± 개발자 λΆ„λ“€κ³Ό ν˜‘μ—…μ„ 진행해 λ³Έ κ²½ν—˜μ΄ μžˆμ§€λ§Œ, 기획 νŒŒνŠΈμ™€μ˜ ν˜‘μ—… κ²½ν—˜μ€ 이번이 μ²˜μŒμ΄μ—ˆλ‹€. 기획 파트 νŒ€μ›λ“€μ˜ μ•„μ΄λ°μ΄μ…˜ 주도, μ™€μ΄μ–΄ν”„λ ˆμž„ μž‘μ—…, νŒ€μ›, μŠ€μΌ€μ€„μ„ κ΄€λ¦¬ν•˜κ³  마무리 λ°œν‘œκΉŒμ§€ν•˜λŠ” 과정을 보며 기획 파트의 역할에 λŒ€ν•΄μ„œλ„ 배울 수 μžˆμ—ˆλ‹€.

 

λ˜ν•œ μ§€λ‚œ 해컀톀, 곡λͺ¨μ „ κ²½ν—˜λ“€ λͺ¨λ‘ μ„œλ²„ 파트λ₯Ό 혼자 λ‹΄λ‹Ήν•˜λŠ” κ²½μš°κ°€ μ „λΆ€μ˜€λŠ”λ° 이번 해컀톀을 톡해 λ‹€λ₯Έ μ„œλ²„ νŒ€μ›λΆ„κ³Ό ν˜‘μ—…ν•˜λŠ” κ²½ν—˜μ„ ν•  수 μžˆμ—ˆλ‹€. μ½”λ“œ μ»¨λ²€μ…˜, κΉƒ μ»¨λ²€μ…˜, 브랜치 μ „λž΅, 이슈 관리, ν”„λ‘œμ νŠΈ 관리, PR 좩돌 ν•΄κ²° λ“± 혼자 μž‘μ—…ν•  λ•ŒλŠ” κ²½ν—˜ν•΄λ³΄μ§€ λͺ»ν•œ 수 λ§Žμ€ ν˜‘μ—… ν”„λ‘œμ„ΈμŠ€λ₯Ό κ²½ν—˜ν•΄ λ³Ό 수 μžˆμ—ˆκ³  이후에 λ‹€λ₯Έ μ„œλ²„ 파트 νŒ€μ›κ³Όμ˜ ν˜‘μ—…λ„ 더 잘 ν•  수 μžˆκ² λ‹€λŠ” μžμ‹ κ°μ„ μ–»κ²Œ 된 계기가 λ˜μ—ˆλ‹€.

 

그리고 λͺ¨λ“  ν˜‘μ—…μ΄ κ·Έλ ‡λ“― μ–Έμ œλ‚˜ κ°€μž₯ μ€‘μš”ν•œ 것은 μ†Œν†΅κ³Ό λ°°λ €μ˜€λ‹€. μ„œλ‘œμ˜ μ˜κ²¬μ— κ·€λ₯Ό 기울이고 μž‘μ—… 상황을 κ³΅μœ ν•˜λ©° λ°°λ €ν•˜λŠ” 마음으둜 λͺ¨λ‘κ°€ ν•¨κ»˜ ν•΄μ•Ό μž‘μ—…μ΄ 즐겁닀. λ‚΄κ°€ μ†ν•œ νŒ€μ˜ νŒ€μ›λ“€μ€ λͺ¨λ‘ μ†Œν†΅κ³Ό λ°°λ €λ₯Ό 잘 ν•΄μ£Όμ—ˆκ³  κ·Έ 덕뢄에 이번 30κΈ° 솝컀톀은 쒋은 μΆ”μ–΅μœΌλ‘œ 남을 것 κ°™λ‹€. 😊