SW/Coding

개발자라면 꼭 알아야 할 서비스 프로바이더 구조 이해하기

얇은생각 2025. 5. 31. 07:30
반응형

개발자 친구에게 들려주는 서비스 프로바이더 이야기

개발하다 보면, 코드가 점점 엉켜서 뭔가 건드릴 때마다 터질 것 같은 느낌… 다들 한 번쯤은 겪어봤죠? 저도 그랬어요. 작은 기능 하나 넣었는데, 그게 왜 이리 복잡하게 꼬였는지 모르겠더라고요. 그래서 어느 날부터 코드를 정리하는 데 진심을 담기 시작했고, 그때 만난 개념이 바로 ‘서비스 프로바이더’였어요. 처음엔 이름부터 좀 거창해 보여서 거리감을 느꼈지만, 막상 알고 나니 “아, 이거 진작 쓸걸!” 싶었죠.

 


 

서비스 프로바이더가 뭐냐고?

 

서비스 프로바이더가 뭐냐고?

  • 앱에서 복잡한 로직은 따로 떼어내서 처리하는 구조예요.
  • 코드가 깔끔해지고, 나중에 유지보수할 때도 스트레스가 훨씬 줄어요.
  • 크게 세 가지로 나뉘어요: 모델, 서비스, 그리고 유틸리티.

 

예전에 저는 컨트롤러 하나에 로직을 다 집어넣었거든요. 근데 프로젝트가 커지니까 코드가 미로 같아졌어요. 어디서 뭘 바꿔야 하는지 찾는 데만 한참 걸리고… 그러다 보니 자꾸 귀찮아지고, 결국 새로운 기능 넣는 게 무서워지더라고요. 그걸 해결해준 게 바로 서비스 프로바이더였어요. ‘일은 분산해서, 책임은 명확하게’라는 느낌이랄까요?

 

 


 

1. 모델(Model): 데이터를 담는 그릇이자 조작하는 도구

 

1. 모델(Model): 데이터를 담는 그릇이자 조작하는 도구

  • 데이터 구조와 그걸 다루는 방법을 정의해요.
  • 사용 사례에서 중요한 단어들(예: user, form 등)을 중심으로 모델을 만들어요.
  • UI 요소나 실제 앱의 기능들과 맞닿아 있어요.

 

모델은 프로젝트 시작할 때 거의 처음으로 정리하는 부분이에요. 저 같은 경우, 팀원들이랑 화이트보드 앞에 모여서 “이 앱에는 어떤 요소들이 있어야 할까?” 하면서 하나하나 이야기해보면서 만들었어요. 예상보다 재밌고 창의적인 과정이었죠.

예를 들어 회원가입 기능이 있다고 하면, 당연히 '사용자(User)'랑 '폼(Form)'이라는 모델이 필요하겠죠? 여기서 사용자 모델은 DB랑 연결돼서 실제 데이터를 저장하고, 폼 모델은 입력값이 제대로 들어왔는지를 체크해주는 역할을 해요.

class FormModel:
    def __init__(self, form):
        self.form = form

    def errors(self):
        errors = []
        if not self.form['username'] or not self.form['username'].isalnum():
            errors.append("Invalid username")
        if not self.form['email']:
            errors.append("Email is required")
        return errors

 

폼 모델은 말 그대로 입력값이 유효한지만 봐요. 컨트롤러는 그저 이 모델한테 “오류 있어?”라고 물어보고, 결과만 보고 판단하면 끝.

class UserModel:
    def __init__(self):
        pass

    def create_user(self, data):
        try:
            user = User(**data)
            db.session.add(user)
            db.session.commit()
            return user
        except IntegrityError:
            return False

 

사용자 모델은 진짜 ‘사용자’를 DB에 저장하는 일만 해요. 그리고 중복되는 이메일 같은 문제가 있으면 조용히 False를 돌려줘요. 실수로 예외 처리 안 해놓으면 큰일 나는 부분인데, 이렇게 분리해두니까 훨씬 깔끔하더라고요.

 


 

 

2. 서비스(Service): 프로젝트마다 달라지는 핵심 로직 담당자

이제 모델처럼 고정된 데이터 말고, 상황에 따라 바뀌는 비즈니스 로직을 다루는 서비스 얘기 좀 해볼게요. 예전에는 컨트롤러 안에 로그인 처리며, 이메일 발송이며 이것저것 다 넣었는데요, 지금은 각 서비스에 나눠서 맡기고 있어요. 정말 숨통 트이는 기분이에요.

 

사용자 인증 서비스

로그인 시 사용자를 세션에 저장해주는 아주 기본적인 서비스예요:

def authenticate_user(user):
    session['user_id'] = user.id
    session['email'] = user.email

 

이건 거의 매일 쓰는 코드인데, 예전엔 계속 복붙하다가 실수도 많았거든요. 이렇게 함수로 빼두니까 너무 편해요.

 

 

이메일 전송 서비스

진짜로 메일 보내는 서비스예요. 그냥 SMTP 쓰는 건데도, 매번 쓰다 보면 헷갈리고 귀찮잖아요?

import smtplib

def send_email(from_email, to_email, subject, content):
    with smtplib.SMTP(settings.SMTP_SERVER) as server:
        server.login(settings.SMTP_USER, settings.SMTP_PASS)
        message = f"Subject: {subject}\n\n{content}"
        server.sendmail(from_email, to_email, message)

 

이렇게 해두면 컨트롤러에서는 그냥 '메일 보내줘~' 하고 필요한 값만 넘기면 돼요. 마치 택배 보내는 느낌이랄까요.

 


 

3. 유틸리티(Utilities): 꼭 필요한 작은 도구 상자

 

3. 유틸리티(Utilities): 꼭 필요한 작은 도구 상자

유틸리티는 말 그대로 도구 모음이에요. 코드 작성할 때 “이거 자주 쓰는데?” 싶은 것들을 모아두는 거죠. 저는 개인적으로 이런 거 모으는 걸 좋아해서, utils.py 파일은 늘 애정이 담겨 있어요.

 

예를 들면 이런 거요:

import uuid

def token_string():
    return str(uuid.uuid4())

def encrypt_string(value):
    return encrypt_protocol(value)

def passwords_match(pw1, pw2):
    return pw1 == pw2

 

간단하죠? 근데 이런 함수들이 생각보다 엄청 유용해요. 특히 비밀번호 암호화 같은 건 실수하면 안 되는 부분이라, 꼭 함수로 빼서 테스트도 해두는 편이에요.

 


 

서비스 프로바이더, 그냥 믿고 써보세요

 

서비스 프로바이더, 그냥 믿고 써보세요

  • 복잡한 건 맡기고, 컨트롤러는 흐름만 챙기세요.
  • 너무 많이 연결하지 말고, 적당히 한두 단계에서 마무리하는 게 좋아요.
  • 심플하고 읽기 쉬운 코드가 결국 오래 가요.

 

서비스 프로바이더를 저는 ‘작은 블랙박스’라고 불러요. 안에서 뭘 하든 간에, 겉에서 봤을 땐 결과만 알 수 있으면 된다는 거죠. 사실 내부 구현은 나중에 바뀌어도, 외부에는 영향 없게 만드는 게 제일 좋잖아요.

예전에 서비스끼리 너무 복잡하게 얽히게 만들었다가, 뭐 하나 고치면 연쇄 폭발 나던 기억이 아직도 생생해요. 그래서 요즘은 ‘2단계 넘기지 말자’를 스스로 룰처럼 지키고 있어요.

 


 

마무리하며: 이 구조, 생각보다 삶의 질 올려줍니다

  • 각 파트를 나눠놓으면 진짜로 마음이 편해져요.
  • 코드는 더 읽기 쉬워지고, 팀원이 새로 들어와도 설명하기가 훨씬 수월해요.
  • “내가 뭘 해야 하지?”가 아니라 “이건 어디 맡기면 되지?”가 되니까 작업 속도도 빨라져요.

 

저는 이 구조 도입하고 나서 팀원들이 제 코드를 칭찬해주더라고요. ‘깔끔하다’, ‘보기 좋다’는 말은 언제 들어도 기분 좋잖아요. 결국 좋은 코드는 혼자 만드는 게 아니라, 함께 성장하는 과정이니까요.

다음엔 이 모든 걸 사용자에게 어떻게 보여줄지, 뷰(View)에 대해 이야기해볼게요. 뒷단이 아무리 좋아도, 앞단에서 매끄럽게 보여주지 못하면 무용지물이니까요. 😉

반응형