@extends('layouts.app') @section('title', '백엔드 개발') @push('styles') @endpush @section('content')
아카데미 백엔드 개발

백엔드 개발

서버, 데이터베이스, API부터 배포까지 — 비개발자도 이해하는 백엔드 가이드

3계층 아키텍처
{{-- ============================================================ --}} {{-- 1. 백엔드란? --}} {{-- ============================================================ --}}

1 백엔드란?

프론트엔드 vs 백엔드 ?3계층(3-Tier) 아키텍처란 프레젠테이션(화면), 애플리케이션(로직), 데이터(DB)를 분리하는 설계 방식이다. 각 계층은 독립적으로 수정·확장할 수 있다.

백엔드(Back-end)란 사용자 눈에 보이지 않는 서버 측 영역이다. 데이터를 저장하고, 비즈니스 규칙을 처리하고, 보안을 관리하고, API를 통해 프론트엔드에 데이터를 전달하는 역할을 한다.

비유: 공장의 생산라인

백엔드는 공장의 생산라인이다. 고객은 완성된 제품(화면)만 보지만, 그 뒤에서는 원자재(데이터) 입고, 가공(비즈니스 로직), 품질검사(유효성 검증), 출하(API 응답)가 이루어진다. 프론트엔드가 매장(쇼룸)이라면, 백엔드는 그 뒤의 공장 전체다.

프론트엔드 (매장)

사용자가 보는 화면
HTML, CSS, JavaScript
버튼, 폼, 메뉴, 테이블

백엔드 (공장)

서버에서 처리하는 로직
PHP, Laravel, MySQL
데이터 저장, 인증, API

백엔드의 4가지 역할

1. 데이터 저장/관리

주문, 고객, 제품 정보를 데이터베이스에 저장하고 필요할 때 꺼내온다.

2. 비즈니스 로직 처리

견적 계산, 재고 차감, 할인 적용 등 업무 규칙을 코드로 구현한다.

3. 보안/인증

로그인, 권한 확인, 비밀번호 암호화 등 보안을 책임진다.

4. API 제공

프론트엔드(앱, 웹)에 데이터를 전달하는 창구를 만들어 제공한다.

SAM 시스템의 백엔드: SAM은 API 서버(Laravel)가 백엔드를 담당한다. MNG(관리자 웹)와 React 앱이 API를 통해 데이터를 받고, MySQL 데이터베이스에 주문·견적·재고 등 모든 데이터가 저장된다.

3계층 아키텍처 다이어그램

3계층 아키텍처 — 브라우저, API 서버, 데이터베이스

{{-- ============================================================ --}} {{-- 2. 서버와 클라이언트 --}} {{-- ============================================================ --}}

2 서버와 클라이언트

HTTP 요청과 응답 ?HTTP는 HyperText Transfer Protocol. 웹에서 데이터를 주고받는 약속(규약)이다. 브라우저가 서버에 "이것 주세요"라고 요청하면, 서버가 "여기 있습니다"라고 응답한다.

비유: 편지 주고받기

클라이언트(브라우저)가 서버에 편지(요청)를 보낸다. 편지 봉투에는 받는 주소(URL), 요청 종류(GET/POST), 내용이 들어 있다. 서버는 편지를 읽고 처리한 뒤, 답장(응답)을 보낸다. 답장에는 상태 코드(200 성공, 404 없음)와 데이터가 들어 있다.

HTTP 메서드 역할 비유
GET 데이터 조회 서류 열람 요청
POST 데이터 생성 새 주문서 제출
PUT 데이터 전체 수정 주문서 전체 다시 작성
DELETE 데이터 삭제 주문 취소

URL 구조와 HTTPS

// URL 구조 분해 https://api.sam-erp.com/api/v1/orders/123?status=active // https → 프로토콜 (암호화 통신) // api.sam-erp.com → 도메인 (서버 주소) // /api/v1 → API 접두사 + 버전 // /orders → 자원 (Resource) // /123 → 특정 자원의 ID // ?status=active → 쿼리 파라미터 (필터)

SAM의 API 통신: SAM은 HTTPS로 암호화된 통신을 사용한다. API 서버 주소는 별도 도메인으로 운영되며, MNG와 React 앱이 REST API를 통해 데이터를 주고받는다.

요청-응답 사이클

요청-응답 사이클 — 클라이언트와 서버의 대화

{{-- ============================================================ --}} {{-- 3. 데이터베이스 기초 --}} {{-- ============================================================ --}}

3 데이터베이스 기초

RDB vs NoSQL ?RDB(관계형 데이터베이스)는 엑셀처럼 행과 열로 데이터를 저장한다. NoSQL은 JSON 문서, 키-값 쌍 등 유연한 형태로 저장한다. SAM은 MySQL(RDB)을 사용한다.

비유: 엑셀 파일

관계형 데이터베이스는 잘 정리된 엑셀 파일과 같다. 각 시트(테이블)에 제목 행(컬럼)이 있고, 각 행(레코드)에 데이터가 들어간다. 시트 간에 "주문번호"같은 공통 항목으로 연결(관계)할 수 있다.

RDB (관계형)

  • • 행과 열로 구성된 테이블
  • • SQL 언어로 데이터 조작
  • • 테이블 간 관계(FK) 설정
  • • 예: MySQL, PostgreSQL

NoSQL (비관계형)

  • • JSON 형태로 유연하게 저장
  • • 대량 데이터에 강함
  • • 스키마 없이 자유로운 구조
  • • 예: MongoDB, Redis

데이터베이스의 핵심 개념 세 가지: PK(Primary Key)는 각 행을 고유하게 식별하는 번호(사원번호), FK(Foreign Key)는 다른 테이블을 참조하는 연결고리(부서코드), 인덱스는 빠른 검색을 위한 색인이다.

SQL CRUD와 인덱스 ?인덱스는 특정 컬럼의 값과 위치를 미리 정리해둔 목록이다. 책 뒷면의 색인처럼, 전체를 훑지 않고 원하는 데이터를 빠르게 찾을 수 있다.

-- CREATE: 데이터 생성 INSERT INTO orders (customer_name, total) VALUES ('주일산업', 1500000); -- READ: 데이터 조회 SELECT * FROM orders WHERE status = 'active'; -- UPDATE: 데이터 수정 UPDATE orders SET status = 'shipped' WHERE id = 123; -- DELETE: 데이터 삭제 DELETE FROM orders WHERE id = 123;

SAM의 데이터베이스: SAM은 MySQL 8.0을 사용하며, tenant_id 컬럼으로 회사(테넌트)별 데이터를 격리한다. 주문, 견적, 품목, 사용자 등 모든 데이터가 관계형 테이블에 저장된다.

ERD 테이블 관계도

ERD 테이블 관계 — 주문, 주문항목, 품목

{{-- ============================================================ --}} {{-- 4. Laravel 프레임워크 --}} {{-- ============================================================ --}}

4 Laravel 프레임워크

MVC 패턴 ?미들웨어는 요청이 컨트롤러에 도달하기 전에 거치는 관문이다. 로그인 확인, 권한 검사, 요청 로깅 등을 처리한다. 공장의 보안 검색대와 같다.

비유: 공장 부서 분리

Model(모델)은 자재 창고 — 데이터를 보관하고 꺼내준다. View(뷰)는 쇼룸 — 완성품을 예쁘게 진열한다. Controller(컨트롤러)는 공장장 — 주문을 받아 창고에서 자재를 꺼내고, 가공해서, 쇼룸에 진열하도록 지시한다.

Model (모델)

데이터 = DB 테이블
데이터 조회·저장·수정

View (뷰)

화면 = HTML 템플릿
데이터를 사용자에게 표시

Controller (컨트롤러)

중재자 = 비즈니스 로직
요청 처리, 모델·뷰 연결

// routes/web.php — URL과 컨트롤러 연결 Route::get('/orders', [OrderController::class, 'index']); // Controller — 요청을 처리하는 중재자 class OrderController { public function index() { $orders = Order::where('status', 'active')->get(); return view('orders.index', compact('orders')); } }

Eloquent ORM과 Artisan ?ORM(Object-Relational Mapping)은 SQL을 직접 쓰지 않고 PHP 코드로 데이터베이스를 다루는 기술이다. 통역사처럼 PHP 언어를 SQL로 자동 변환해준다.

// SQL 직접 작성 SELECT * FROM orders WHERE status = 'active' ORDER BY created_at DESC; // Eloquent ORM — 같은 결과를 PHP로 $orders = Order::where('status', 'active') ->orderBy('created_at', 'desc') ->get();

Artisan 주요 명령어

php artisan migrate — DB 테이블 생성/수정

php artisan make:controller — 컨트롤러 생성

php artisan make:model — 모델 생성

php artisan cache:clear — 캐시 초기화

php artisan tinker — 대화형 PHP 콘솔

SAM의 Laravel: SAM은 Laravel 11 + PHP 8.3을 사용한다. MNG와 API 모두 Laravel 프레임워크 기반이며, Eloquent ORM으로 MySQL 데이터를 다루고, Artisan 명령어로 마이그레이션·캐시 관리를 한다.

MVC 흐름도

MVC 흐름도 — Route → Controller → Model → View

{{-- ============================================================ --}} {{-- 5. API 설계 --}} {{-- ============================================================ --}}

5 API 설계

REST 원칙과 JSON ?REST는 URL로 자원(Resource)을 표현하고 HTTP 메서드로 행위를 나타내는 설계 방식이다. /orders는 주문 자원, GET은 조회, POST는 생성 행위를 의미한다.

비유: 주문 창구

API는 주문 창구다. 메뉴판(API 문서)을 보고 정해진 양식(JSON)으로 주문(요청)하면, 주방(서버)에서 요리(처리)해서 음식(데이터)을 내준다. 아무렇게나 주문하면 안 되고, 정해진 형식을 따라야 한다.

메서드 URL 동작
GET /api/v1/orders 주문 목록 조회
GET /api/v1/orders/123 주문 #123 상세 조회
POST /api/v1/orders 새 주문 생성
PUT /api/v1/orders/123 주문 #123 수정
DELETE /api/v1/orders/123 주문 #123 삭제
// JSON 응답 예시 { "data": { "id": 123, "customer": "주일산업", "total": 1500000, "status": "active", "items": [ { "name": "방화셔터 A형", "qty": 5 } ] } }

API 인증과 버전 관리 ?Sanctum은 Laravel 공식 인증 패키지다. 모바일 앱과 SPA를 위한 토큰 발급, API 인증을 간단하게 구현한다. 회원증을 발급받아 매번 보여주는 것과 같다.

// Bearer Token 인증 — 회원증 제시 GET /api/v1/orders Authorization: Bearer eyJhbGciOiJIUzI1NiIs... // 버전 관리 — API가 바뀌어도 기존 앱이 동작 /api/v1/orders ← 현재 버전 /api/v2/orders ← 새 버전 (구조 변경)

SAM의 API 인증: SAM API는 /api/v1/ 접두사를 사용하고, Laravel Sanctum으로 토큰 인증을 처리한다. React 앱에서 로그인하면 토큰을 발급받아 이후 모든 API 요청에 포함한다.

REST API 구조

REST API 구조 — URL 분해와 JSON 응답

{{-- ============================================================ --}} {{-- 6. 인증과 보안 --}} {{-- ============================================================ --}}

6 인증과 보안

세션 vs 토큰 ?bcrypt는 비밀번호를 되돌릴 수 없는 형태로 변환(해싱)하는 알고리즘이다. 같은 비밀번호라도 매번 다른 해시값을 생성하여 보안성이 높다.

비유: 도장카드 vs 출입배지

세션도장카드다. 가게(서버)에 내 도장카드를 보관해두고, 올 때마다 번호표(세션ID)만 보여준다. 가게가 기록을 관리한다. 토큰출입배지다. 내가 배지(JWT)를 가지고 다니며, 입장할 때마다 배지를 보여준다. 건물(서버)은 배지 진위만 확인한다.

세션 (Session)

  • • 서버에 상태 저장 (Stateful)
  • • 쿠키로 세션 ID 전달
  • • 서버 메모리/DB 사용
  • • SAM MNG에서 사용

토큰 (Token)

  • • 서버에 상태 미저장 (Stateless)
  • • Authorization 헤더로 전달
  • • 확장성이 좋음 (서버 무상태)
  • • SAM API에서 사용 (Sanctum)

보안 위협과 방어

SQL Injection

사용자 입력에 악의적 SQL을 삽입하는 공격

방어: 파라미터 바인딩 (Eloquent 자동 처리)

CSRF

사용자 모르게 악의적 요청을 보내는 공격

방어: CSRF 토큰 검증 (Laravel 자동 처리)

SAM의 보안: SAM MNG는 세션 기반 인증 + CSRF 토큰으로 보안을 처리하고, API 서버는 Sanctum 토큰 인증을 사용한다. 비밀번호는 bcrypt로 해싱하여 저장한다.

세션 vs 토큰 비교

인증 비교 — 세션 방식 vs 토큰 방식

{{-- ============================================================ --}} {{-- 7. 비즈니스 로직 --}} {{-- ============================================================ --}}

7 비즈니스 로직

서비스 레이어와 유효성 검증 ?서비스 레이어는 컨트롤러에서 비즈니스 로직을 분리한 클래스다. 컨트롤러가 "무엇을 할지" 결정하면, 서비스가 "어떻게 할지" 실행한다.

비유: 회사 규정

비즈니스 로직은 회사 규정이다. "견적 금액이 100만원 이상이면 팀장 승인 필요", "재고가 0이면 주문 불가" 같은 규칙을 코드로 구현한 것이다. 규정이 바뀌면 서비스 클래스만 수정하면 된다.

// Validation — 입력값 검증 $request->validate([ 'customer_name' => 'required|max:100', 'email' => 'required|email', 'total' => 'required|numeric|min:0', ]); // Event/Listener — 이벤트 기반 처리 // 주문 생성 → 이벤트 발생 → 리스너가 알림 발송 event(new OrderCreated($order)); // → SendOrderNotification 리스너가 자동 실행

트랜잭션 ?트랜잭션은 여러 DB 작업을 하나의 단위로 묶는 것이다. 모두 성공하면 확정(commit), 하나라도 실패하면 전부 취소(rollback)한다.

비유: 은행 이체

A 계좌에서 10만원을 빼고 B 계좌에 10만원을 넣는 이체를 생각해보자. A에서 빼기는 성공했는데 B에 넣기가 실패하면? 돈이 사라진다! 트랜잭션은 두 작업을 하나로 묶어 "둘 다 성공하거나, 둘 다 취소"하도록 보장한다.

// Laravel 트랜잭션 예시 DB::transaction(function () { // 1. A 계좌에서 출금 $accountA->decrement('balance', 100000); // 2. B 계좌에 입금 $accountB->increment('balance', 100000); // 둘 다 성공 → 자동 commit // 하나라도 실패 → 자동 rollback });

SAM의 비즈니스 로직: SAM은 견적 계산, 주문 처리, 재고 관리 등의 비즈니스 로직을 서비스 클래스에서 처리한다. 주문 생성 시 재고 차감, 이력 기록 등을 트랜잭션으로 묶어 데이터 무결성을 보장한다.

트랜잭션 다이어그램

트랜잭션 — 성공(commit)과 실패(rollback)

{{-- ============================================================ --}} {{-- 8. 큐와 비동기 처리 --}} {{-- ============================================================ --}}

8 큐와 비동기 처리

동기 vs 비동기 ?큐는 선입선출(FIFO) 방식의 작업 대기열이다. 은행 대기표처럼 먼저 들어온 작업부터 순서대로 처리한다.

비유: 대기표 시스템

동기 처리는 은행 창구에서 줄 서는 것 — 앞 사람이 끝날 때까지 기다려야 한다. 비동기 처리는 대기표를 받는 것 — 번호표를 받고 자기 볼일을 보다가, 호출되면 처리한다. 이메일 100통을 보낼 때, 동기면 30초 기다려야 하지만 비동기(큐)면 즉시 "발송 예약됨"이 표시된다.

동기 처리 (Synchronous)

순서대로 하나씩 처리
앞 작업 끝나야 다음 시작
사용자가 기다려야 함

비동기 처리 (Asynchronous)

큐에 넣고 즉시 응답
워커가 백그라운드에서 처리
사용자는 기다리지 않음

큐/워커와 스케줄러

// Job 클래스 — 큐에 넣을 작업 단위 class SendAlimtalk implements ShouldQueue { public function handle() { // 알림톡 발송 로직 AlimtalkService::send($this->phone, $this->message); } } // 큐에 작업 넣기 SendAlimtalk::dispatch($phone, $message); // → 즉시 응답, 워커가 나중에 처리 // 스케줄러 — 매일 새벽 2시에 백업 $schedule->command('backup:run')->dailyAt('02:00');

SAM의 큐 활용: SAM은 알림톡 발송, PDF 생성, 이메일 발송 등을 큐로 비동기 처리한다. Docker 컨테이너 안에서 Queue Worker가 상시 실행되며, Scheduler로 정기 작업을 자동 실행한다.

큐 처리 흐름

큐 처리 — 요청 → 큐 → 워커 → 완료

{{-- ============================================================ --}} {{-- 9. 캐싱과 성능 최적화 --}} {{-- ============================================================ --}}

9 캐싱과 성능 최적화

캐시의 원리 ?캐시는 자주 사용하는 데이터를 빠른 저장소에 임시 보관하는 기법이다. DB에서 매번 조회하는 대신 캐시에서 바로 꺼내면 응답 속도가 수십 배 빨라진다.

비유: 책상 위 필기도구

자주 쓰는 볼펜과 메모지는 책상 위(캐시)에 놓아둔다. 필요할 때 바로 꺼내 쓸 수 있다. 가끔 쓰는 도구는 서랍(데이터베이스)에 보관한다. 서랍을 열고 찾는 데 시간이 더 걸린다. 캐시는 이처럼 자주 쓰는 데이터를 가까이 두어 빠르게 접근하는 기법이다.

// Laravel 캐시 사용 // 캐시에 있으면 바로 반환, 없으면 DB에서 조회 후 캐시에 저장 $orders = Cache::remember('active_orders', 3600, function () { return Order::where('status', 'active')->get(); }); // 3600 = 1시간(초) 동안 캐시 유지

N+1 문제와 Eager Loading ?N+1 문제는 1번의 쿼리로 목록을 가져온 뒤, 각 항목마다 추가 쿼리를 실행하는 것이다. 100개 주문을 조회하면 101번의 쿼리가 발생한다.

N+1 문제 (느림)

1. SELECT * FROM orders

2. SELECT * FROM items WHERE order_id=1

3. SELECT * FROM items WHERE order_id=2

... (N번 반복)

총 101번 쿼리!

Eager Loading (빠름)

1. SELECT * FROM orders

2. SELECT * FROM items WHERE order_id IN (1,2,...)

총 2번 쿼리!

// N+1 문제 발생 $orders = Order::all(); // 1번 쿼리 foreach ($orders as $order) { $order->items; // N번 추가 쿼리! } // Eager Loading으로 해결 $orders = Order::with('items')->get(); // 2번 쿼리로 끝!

SAM의 성능 최적화: SAM은 config, route 캐싱으로 응답 속도를 최적화하고, Eager Loading으로 N+1 문제를 방지한다. DB 인덱스를 적절히 설정하여 대량 데이터 조회 성능을 확보한다.

캐시 계층 다이어그램

캐시 계층 — 캐시 히트와 캐시 미스

{{-- ============================================================ --}} {{-- 10. 배포와 운영 --}} {{-- ============================================================ --}}

10 배포와 운영

환경 분리 ?.env 파일은 DB 접속 정보, API 키 등 환경마다 달라지는 설정을 모아둔 파일이다. 절대 Git에 커밋하면 안 된다.

비유: 시험생산 → 품질검사 → 출하

공장에서 신제품을 바로 출하하지 않는다. 시험생산(Local)에서 만들어보고, 품질검사(Staging)를 통과하면, 비로소 출하(Production)한다. 소프트웨어도 마찬가지로 환경을 분리하여 안전하게 배포한다.

Local (개발)

개발자 PC
자유롭게 실험
Docker 컨테이너

Staging (테스트)

운영과 동일한 환경
최종 검증
실 데이터 없음

Production (운영)

실제 서비스
실 사용자 접속
장애 시 즉시 대응

Docker 배포와 모니터링

# SAM 배포 절차 (간략) # 1. 코드 작성 & 커밋 git add . && git commit -m "feat: 새 기능 추가" # 2. 코드 푸시 git push origin develop # 3. 서버에서 코드 받기 git pull origin develop # 4. 의존성 설치 & 마이그레이션 composer install php artisan migrate php artisan config:clear

Git 브랜치 전략

  • develop — 개발 진행 브랜치
  • main — 운영 배포 브랜치
  • feature/* — 기능 개발 브랜치
  • hotfix/* — 긴급 수정 브랜치

로그와 모니터링

  • storage/logs/laravel.log — 앱 로그
  • nginx error.log — 웹서버 로그
  • php artisan queue:work — 큐 상태
  • docker ps — 컨테이너 상태

SAM의 배포: SAM은 Docker Compose로 로컬 개발 환경(MNG, API, MySQL, Nginx)을 구성하고, 운영 서버에는 직접 배포한다. develop 브랜치에서 개발을 진행하며, 코드 포맷팅(Pint), 캐시 클리어, 마이그레이션을 거쳐 배포한다.

배포 파이프라인

배포 파이프라인 — 코드 → Git → 빌드 → 서버

핵심 정리

백엔드는 "사용자 눈에 보이지 않는 서버의 세계"다. HTTP로 요청을 받아, 데이터베이스에서 데이터를 꺼내고, 비즈니스 로직을 처리하여 API로 응답한다. Laravel 프레임워크가 MVC 패턴, ORM, 미들웨어, 큐, 캐시 등 핵심 기능을 제공하고, 세션·토큰으로 인증, 트랜잭션으로 데이터 무결성, Eager Loading으로 성능을 보장한다. Docker로 개발 환경을 통일하고, Git으로 버전을 관리하여 안전하게 배포한다. SAM은 이 모든 백엔드 기술을 활용하여 ERP/MES 통합 시스템을 운영하고 있다.

@include('components.academy-glossary', ['domain' => 'backend-dev']) @endsection