Files
sam-docs/frontend/api-specs/income-statement-service-api.md

13 KiB

손익계산서 API 명세 (서비스 이관)

작성일: 2026-03-20 상태: API 완료, React 구현 대기 대상: React 프론트엔드 개발자


1. 개요

회계관리 메뉴에 손익계산서 화면을 구현한다. 기간별 당기/전기 비교와 월별 비교 두 가지 보기 모드를 제공한다. 한국 일반기업회계기준에 따른 10개 항목(I~X) 구조로 매출액, 매출원가, 영업이익, 당기순이익 등을 표시한다.

메뉴 위치

회계관리
├── 일반전표입력       ← 기존
├── 계정별원장         ← 기존
├── 손익계산서         ← 이 화면
├── ...

2. API 엔드포인트

2.1 기간별 손익계산서

GET /api/v1/income-statement

요청 파라미터 (Query String):

파라미터 타입 필수 설명 예시
start_date string(date) Y 당기 시작일 2026-01-01
end_date string(date) Y 당기 종료일 2026-12-31
unit string N won(기본) / thousand / million won

전기: API가 자동으로 전년 동기를 계산 (예: 2026 → 2025)

응답 예시:

{
  "success": true,
  "message": "조회되었습니다.",
  "data": {
    "period": {
      "current": {
        "start": "2026-01-01",
        "end": "2026-12-31",
        "label": "제 2 (당)기"
      },
      "previous": {
        "start": "2025-01-01",
        "end": "2025-12-31",
        "label": "제 1 (전)기"
      }
    },
    "unit": "won",
    "sections": [
      {
        "code": "I",
        "name": "매출액",
        "current_amount": 50000000,
        "previous_amount": 30000000,
        "items": [
          { "code": "40400", "name": "용역매출", "current": 50000000, "previous": 30000000 }
        ],
        "is_calculated": false
      },
      {
        "code": "II",
        "name": "매출원가",
        "current_amount": 20000000,
        "previous_amount": 15000000,
        "items": [
          { "code": "50100", "name": "서비스매출원가", "current": 12000000, "previous": 8000000 },
          { "code": "50200", "name": "외주용역비", "current": 8000000, "previous": 7000000 }
        ],
        "is_calculated": false
      },
      {
        "code": "III",
        "name": "매출총이익",
        "current_amount": 30000000,
        "previous_amount": 15000000,
        "items": [],
        "is_calculated": true
      }
    ]
  }
}

2.2 월별 손익계산서

GET /api/v1/income-statement/monthly

요청 파라미터 (Query String):

파라미터 타입 필수 설명 예시
year integer Y 조회 연도 2026
unit string N won(기본) / thousand / million won

응답 예시:

{
  "success": true,
  "message": "조회되었습니다.",
  "data": {
    "year": 2026,
    "fiscal_year": 2,
    "fiscal_label": "제 2 기",
    "unit": "won",
    "months": [
      {
        "month": "01",
        "label": "1월",
        "sections": [
          {
            "code": "I",
            "name": "매출액",
            "current_amount": 5000000,
            "previous_amount": 5000000,
            "items": [
              { "code": "40400", "name": "용역매출", "current": 5000000, "previous": 5000000 }
            ],
            "is_calculated": false
          }
        ]
      },
      {
        "month": "02",
        "label": "2월",
        "sections": []
      }
    ]
  }
}

참고: 미래 월은 응답에 포함되지 않는다. 월별 데이터에서 previous_amountitem.previouscurrent와 동일한 값 (월별 비교에서는 의미 없음).


3. TypeScript 인터페이스

// 기간별 손익계산서 응답
interface IncomeStatementResponse {
  success: boolean;
  message: string;
  data: {
    period: {
      current: { start: string; end: string; label: string };   // "제 2 (당)기"
      previous: { start: string; end: string; label: string };  // "제 1 (전)기"
    };
    unit: 'won' | 'thousand' | 'million';
    sections: PLSection[];
  };
}

// 월별 손익계산서 응답
interface MonthlyIncomeStatementResponse {
  success: boolean;
  message: string;
  data: {
    year: number;
    fiscal_year: number;
    fiscal_label: string;        // "제 2 기"
    unit: 'won' | 'thousand' | 'million';
    months: MonthPL[];
  };
}

interface MonthPL {
  month: string;                 // "01", "02", ...
  label: string;                 // "1월", "2월", ...
  sections: PLSection[];
}

interface PLSection {
  code: string;                  // "I", "II", "III", ... "X"
  name: string;                  // "매출액", "매출원가", "매출총이익", ...
  current_amount: number;        // 당기 금액
  previous_amount: number;       // 전기 금액
  items: PLItem[];               // 세부 계정과목 (계산 항목은 빈 배열)
  is_calculated: boolean;        // true = 계산 항목 (III, V, VIII, X)
}

interface PLItem {
  code: string;                  // 계정코드 "40400"
  name: string;                  // 계정명 "용역매출"
  current: number;               // 당기 금액
  previous: number;              // 전기 금액
}

4. 화면 구현 가이드

4.1 화면 구조

┌───────────────────────────────────────────────────┐
│ 📊 손익계산서                               [인쇄] │
├───────────────────────────────────────────────────┤
│ [기간 보기] [월별 보기]   [당기만] [당기+전기]      │
│ 기간: [시작일]~[종료일]  단위: [원▾]  [조회]       │
├───────────────────────────────────────────────────┤
│                                                   │
│  ┌─────────────────────────────────────────────┐  │
│  │ 과  목          │  제 2 (당)기 │  제 1 (전)기│  │
│  │                 │   금  액     │   금  액    │  │
│  ├─────────────────┼──────────────┼─────────────┤  │
│  │ I. 매출액       │              │             │  │
│  │   용역매출      │  50,000,000  │             │  │
│  │                 │              │ 50,000,000  │  │
│  ├─────────────────┼──────────────┼─────────────┤  │
│  │ II. 매출원가    │              │             │  │
│  │   서비스매출원가│  12,000,000  │             │  │
│  │   외주용역비    │   8,000,000  │             │  │
│  │                 │              │ 20,000,000  │  │
│  ├─────────────────┼──────────────┼─────────────┤  │
│  │ III. 매출총이익 │              │ 30,000,000  │  │  ← 강조 (연두)
│  │ ...             │              │             │  │
│  │ X. 당기순이익   │              │ 25,000,000  │  │  ← 강조 (연두)
│  └─────────────────┴──────────────┴─────────────┘  │
│                              (단위: 원)            │
└───────────────────────────────────────────────────┘

4.2 보기 모드 전환

모드 필터 API 설명
기간 보기 시작일~종료일 + 단위 GET /income-statement 당기+전기 2열 비교
월별 보기 연도 + 단위 GET /income-statement/monthly 월별 비교
  • 모드 전환 시 기존 데이터 초기화 후 새로 조회
  • 기본: 기간 보기, 당해 1/1 ~ 12/31

4.3 기간 보기 테이블 (PeriodTable)

컬럼 구성:

상태 컬럼
당기만 과목 | 금액(세부) | 금액(소계)
당기+전기 과목 | 당기(세부) | 당기(소계) | 전기(세부) | 전기(소계)

행 유형:

행 유형 조건 스타일 금액 표시
섹션 헤더 is_calculated === false 굵은 글씨 items 없으면 소계열에 금액, 있으면 빈칸
세부 항목 items[] 들여쓰기(pl-12), 일반 세부열에 금액, 마지막 항목의 소계열에 섹션 합계
계산 항목 is_calculated === true 강조 배경 소계열에만 금액

강조 표시 (emerald-50 배경):

  • III (매출총이익), V (영업이익), VIII (법인세비용차감전순이익), X (당기순이익)

4.4 월별 보기 테이블 (MonthlyTable)

단일 월 선택: 기간 보기와 유사한 1열 테이블 전체 월 선택: 가로 스크롤 비교 테이블

┌──────────────┬──────┬──────┬──────┬─────┐
│ 과목 (sticky)│ 1월  │ 2월  │ 3월  │ ... │
├──────────────┼──────┼──────┼──────┼─────┤
│ I. 매출액    │  5M  │  4M  │  6M  │ ... │
│   용역매출   │  5M  │  4M  │  6M  │ ... │
│ II. 매출원가 │  2M  │  1M  │  3M  │ ... │
│ III. 매출총이│  3M  │  3M  │  3M  │ ... │  ← 강조
│ ...          │      │      │      │     │
└──────────────┴──────┴──────┴──────┴─────┘
  • 과목 열: sticky left-0 (가로 스크롤 시 고정)
  • 최소 너비: months.length * 110 + 200 px

4.5 월 선택 버튼 (월별 보기)

[전체] [1월] [2월] [3월] ... [12월]
  • 데이터가 있는 월만 버튼 표시 (미래 월은 API 응답에 없음)
  • 전체 선택 시 가로 스크롤 비교 테이블
  • 개별 월 선택 시 단일 열 테이블

4.6 숫자 포맷

const fmt = (n: number): string => {
  if (n === 0 || n === null || n === undefined) return '';
  if (n < 0) return '(' + Math.abs(n).toLocaleString() + ')';
  return n.toLocaleString();
};

const UNIT_LABELS: Record<string, string> = {
  won: '원',
  thousand: '천원',
  million: '백만원'
};

4.7 인쇄

  • window.print() 사용
  • 조회 조건, 인쇄 버튼에 no-print 클래스
  • CSS: @media print { .no-print { display: none !important; } }

4.8 기본값

항목 기본값
보기 모드 기간 보기
시작일 당해 1/1
종료일 당해 12/31
단위 원 (won)
당기/전기 당기만
월 선택 전체
  • 페이지 진입 시 기본값으로 자동 조회

5. 손익계산서 항목 구조

I.   매출액                    ← 합계 항목 (세부 계정과목 표시)
II.  매출원가                  ← 합계 항목
III. 매출총이익 = I - II       ← 계산 항목 (is_calculated: true, 강조)
IV.  판매비와관리비            ← 합계 항목
V.   영업이익 = III - IV       ← 계산 항목 (강조)
VI.  영업외수익                ← 합계 항목
VII. 영업외비용                ← 합계 항목
VIII.법인세비용차감전순이익     ← 계산 항목
IX.  법인세비용                ← 합계 항목
X.   당기순이익 = VIII - IX    ← 계산 항목 (강조)
  • 합계 항목: items[]에 세부 계정과목 포함, current_amount/previous_amount는 합계
  • 계산 항목: items[]는 빈 배열, is_calculated: true, 금액은 공식으로 계산된 결과

6. MNG 참고 화면

MNG 개발서버: https://admin.codebridge-x.com → 회계/세무관리 > 손익계산서

파일 설명
mng/app/Http/Controllers/Finance/IncomeStatementController.php 전체 로직 (PL_STRUCTURE, buildSections)
mng/resources/views/finance/income-statement.blade.php React 컴포넌트 (PeriodTable, MonthlyTable)

관련 문서


최종 업데이트: 2026-03-20