F&F 기업협업을 마치며...

이산·2022년 6월 3일

기업협업

목록 보기
2/2

한달동안 F&F 기업에서 기업협업 프로젝트를 진행하며 좋았던 점과 아쉬운 점 등을 솔직하게 남기려고 한다.

너무나도 즐겁기도 했고, 한편으로는 너무 아쉬운 한달간의 기업협업이 끝이났다. 소통의 부족 또는 의견 충돌로 싸우기도 했고, 어려워서 포기하려고도 싶었지만 그래도 부끄럽지 않을 정도로 열심히 노력했고 또 많은 도움을 주신 분들 덕분에 마무리 할 수 있었다.
인원은 백엔드 2명, 프론트 2명으로 총 4명이었고 약 한달정도 진행됬다.

🤝 첫날의 긴장감! 불친절한 회사...?

떨리고 설레는 마음으로 첫날이 시작됬다! 회사에 계신 많은 분들과 좋은 관계를 형성하고 싶은 마음과 새로운 것을 배운다는 기대감에 들떴지만 들어가는 순간 숨이 막힐듯한 긴장감이 다가왔다... 거의 대기업이라고 불려도 이상하지 않을 정도로 큰 기업이라고 그런지 다들 바빠 보이셨고, 크게 환영받지는 못했다. 사실 첫날 이름도 물어보지 않으시고... 전공자가 누구인지만 궁금해 하셔서 서운하긴 했지만 놀러온 것이 아닌만큼 더욱 집중할 수 있었다.

회사는 일을하러 오는 곳이지 배우러 오는 곳이 아니다!

⭐️ 우리가 기획을...??

개발자는 누군가 기획한 기획물을 받아서 일을 한다고 생각하고 있었지만 우리가 처음 시작한 일은 기획이었다. 몇가지 선택지를 받았고 그 중에서 우리가 선택한 것은 인플루언서 컨택 플랫폼이다. 모든 시스템이 갖추어져 있는 대기업이 아닌 중소기업을 타겟으로 했다. 처음에는 사용자의 입장에서 생각하기 보다는 우리의 관점에서 기획을 시작했지만 F&F 부장님이신 로이님과 쿠마님께 여러번 피드백을 받고 고민하다 보니 사용자 입장에서 생각하는 것이 정말 중요하다는 것을 느꼈다.

개발하는 나만의 관점이 아닌 사용자의 관점에서 항상 생각해보자!

🏃🏻‍♂️ 끝없는 개발...

기획을 마무리 하고 개발을 시작하면서 불화가 생겼다. 백엔드 언어를 선택하는 과정에서 나는 개인적으로 회사에서 추천하고 처음 사용해보는 nodeJs, experss를 사용하고 싶었다. 하지만 같은 팀 백엔드 분께서는 지금까지 다뤄왔던 Django, Python으로 개발을 시작하고 싶어하셨다. 시간이 없었기 때문에 주변 멘토님들과 지인들께 조언을 구했고 그 결과 회사에서 원하는 nodeJs, express, typeorm으로 개발을 시작했다.

사실 개발한 내용이 대단하지는 않다. 개발기간은 한달중에 약 1주일이었고 우리는 새로운 언어를 공부하면서 개발을 진행해야 했다. 우리는 서로 잘하는 점을 살려서 팀원분은 DB설계와 데이터 등을 맡으셨고 나는 대부분의 개발을 담당했다.
아래는 내가 직접 개발한 코드이다. 내용이 너무 많기에 몇 가지만 올리려고 한다.

DB 설계

폴더 구조

src
-controller
-entity
-middleware
-migration
-orm
-route

만들고 나서 생각해보면 그렇게 어려운 일은 아니였지만 폴더 구조를 짜고 라우트 설정하는 일이 가장 시간이 오래걸렸다. orm에서는 DB를 컨택하는 부분만 코드를 작성했고 controller에서 클라이언트에 어떤식으로 전달할지 작성했다.

캠페인 controller

import { Response } from 'express';
import { Campaign } from '../entity/Campaign';
import { Message } from '../entity/Message';
import { IGetUserAuthInfoRequest } from '../definition';
import AppDataSource from '../data-source';

class CampaignController {
    async createCampaign(req: IGetUserAuthInfoRequest, res: Response) {
        const { userId, campaign, campaignName } = req;
        if (!campaign) {
            const myCampaign = new Campaign();
            myCampaign.userID = userId;
            myCampaign.campaign_name = campaignName;
            Campaign.getRepository().save(myCampaign);
            return res.status(201).send({ message: 'Success' });
        } else {
            return res.status(400).send({ message: 'Already exist name' });
        }
    }

    async patchCampaign(req: IGetUserAuthInfoRequest, res: Response) {
        const { userId, campaign, campaignName, campaignOne } = req;
        if (!campaign) {
            campaignOne.userID = userId;
            campaignOne.campaign_name = campaignName;
            await Campaign.save(campaignOne);
            return res.status(200).send({ message: 'Success' });
        } else {
            return res.status(406).send({ message: 'Already exist name' });
        }
    }

    async deleteCampaign(req: IGetUserAuthInfoRequest, res: Response) {
        const { messageList: message, campaignOne: campaign } = req;
        if (!!campaign) {
            await Message.remove(message);
            await Campaign.remove(campaign);
            return res.status(200).send({ message: 'Success' });
        } else {
            return res.status(400).send({ message: 'Unauthorized' });
        }
    }

    async deleteInfluencer(req: IGetUserAuthInfoRequest, res: Response) {
        const { userId } = req;
        const { influencerId, campaignId } = req.body;
        for (const id of influencerId) {
            const message = await Message.find({
                relations: { campaign: true },
                where: {
                    influencerID: id,
                    campaign: { id: campaignId, userID: userId },
                },
            });
            if (message) {
                await Message.remove(message);
                continue;
            } else {
                return res.status(405).send({ message: 'Unauthorized' });
            }
        }
        return res.status(200).send({ message: 'Success' });
    }
}

const campaignController = new CampaignController();
export default campaignController;

캠페인 생성 및 삭제 등에 관한 내용이다. 비동기를 사용했고 IGetUserAuthInfoRequest라는 인터페이스를 하나만 사용했는데 나중에는 여러가지로 나누어보려고 한다. 시간이 부족했기 때문에 한가지만 사용했다.

캠페인 orm

import { NextFunction, Response } from 'express';
import { IGetUserAuthInfoRequest } from '../definition';
import { Campaign } from '../entity/Campaign';
import { Message } from '../entity/Message';

class CampaignOrmController {
    async findCampaignWithCampaignName(
        req: IGetUserAuthInfoRequest,
        res: Response,
        next: NextFunction
    ) {
        const { userId } = req;
        const { campaignName } = req.body;
        const campaign = await Campaign.findOne({
            where: { userID: userId, campaign_name: campaignName },
        });
        req.campaign = campaign;
        req.campaignName = campaignName;
        next();
    }

    async findCampiagnWithCampaignId(
        req: IGetUserAuthInfoRequest,
        res: Response,
        next: NextFunction
    ) {
        const userId = req.userId;
        const { campaignName } = req.body;
        const campaignId = parseInt(req.params.campaignid);
        const campaign = await Campaign.findOne({
            where: { id: campaignId, userID: userId },
        });
        req.campaignOne = campaign;
        req.campaignName = campaignName;
        next();
    }

    async findMessageWithCampaignId(
        req: IGetUserAuthInfoRequest,
        res: Response,
        next: NextFunction
    ) {
        const { userId } = req;
        const campaignId = parseInt(req.params.campaignid);
        const message = await Message.find({
            relations: { campaign: true },
            where: {
                campaignID: campaignId,
                campaign: { userID: userId },
            },
        });
        req.messageList = message;
        next();
    }
}

const campaignormcontroller = new CampaignOrmController();
export default campaignormcontroller;

DB에서 필요한 정보를 가져오는 코드이다. 혼자서 구글링해서 개발하다 보니 시간이 정말 오래 걸린 부분 중 하나이다. 프론트에게 더 깔끔하게 정리해서 데이터를 보내주고 싶었는데 생각보다 잘 되지 않아서 아쉬웠다.

자세한 내용은 아래의 깃허브 링크에서 확인할 수 있다.
정말 지치고 힘들고 슬프고 어려웠고... 내가 지금까지 이렇게 열심히 한 적이 있나 고민할 정도로 최선을 다했다. 결국 구현이 성공했고, 내용물이 만족스럽다고는 말하지 못하지만 구현해낸 내 자신에게 너무 자랑스럽다. 함께 고생한 팀원들도 최고였다!!

프로젝트 깃허브 링크
https://github.com/leesan195159/influencer

🔚 마무리하며

얼마 되지 않은 개발자 인생에서 너무 귀한 경험이었다. 이런 경험을 가능하게 해 주신 F&F와 위코드, 또 많은 도움을 주신 멘토님들과 지인분들께 감사드리고 앞으로도 더 노력해서 좋은 개발자가 되도록 노력해야겠다.

profile
백엔드 개발자입니다.

0개의 댓글