본문 바로가기

비개발자의 개발 일지

현장 팀장에서 수출 ERP까지 — AI와 함께 시스템을 만든 기록

시리즈 보기
php

MySQL ADDDATE · SUBDATE — 날짜 더하기·빼기 완전 정리

by 왕진 2026. 6. 27.
반응형

 

 

Database · MySQL

ADDDATE · SUBDATE — 날짜 더하기·빼기 완전 정리

INTERVAL 하나로 며칠 뒤, 몇 달 전을 자유자재로 계산하는 법
시작 오늘 분석할 코드

날짜에 기간을 더하거나 빼는 계산은 회원가입일 + 30일 = 쿠폰 만료일, 오늘 - 1개월 = 지난달 통계 기준일처럼 실무에서 정말 자주 나온다. 아래 코드는 ADDDATESUBDATEINTERVAL 표현식을 넘겨 "며칠 뒤"와 "몇 달 뒤"를 동시에 구하는 가장 기본적인 형태다.

SELECT ADDDATE('2025-01-01', INTERVAL 31 DAY), ADDDATE('2025-01-01', INTERVAL 1 MONTH); -- 31일 뒤 / 1 달 뒤 SELECT SUBDATE('2025-01-01', INTERVAL 31 DAY), SUBDATE('2025-01-01', INTERVAL 1 MONTH); -- 31일 전 / 1 달 전
실제 결과 미리보기
계산식 기준일 연산 결과
ADDDATE(..., INTERVAL 31 DAY) 2025-01-01 +31일 2025-02-01
ADDDATE(..., INTERVAL 1 MONTH) 2025-01-01 +1개월 2025-02-01
SUBDATE(..., INTERVAL 31 DAY) 2025-01-01 -31일 2024-12-01
SUBDATE(..., INTERVAL 1 MONTH) 2025-01-01 -1개월 2024-12-01
용어 용어 정리
ADDDATE기준 날짜에 기간을 더한 날짜를 반환하는 함수
SUBDATE기준 날짜에서 기간을 뺀 날짜를 반환하는 함수
INTERVAL"숫자 + 단위" 형태로 기간의 크기를 나타내는 표현식
DATE_ADDADDDATE와 완전히 동일하게 동작하는 표준 별칭 함수
DATE_SUBSUBDATE와 완전히 동일하게 동작하는 표준 별칭 함수
DAY / MONTH / YEARINTERVAL 뒤에 붙는 기간 단위 키워드
CURDATE()오늘 날짜를 반환 — 기준일 자리에 실전에서 자주 대입
DATETIME날짜+시간 값 — HOUR·MINUTE 단위 연산 시 결과 타입
NOW()현재 날짜와 시각을 함께 반환 — 날짜만 필요하면 CURDATE()
LAST_DAY()해당 월의 마지막 날짜를 반환 — 월말 보정 로직에 함께 자주 쓰임

1 ADDDATE 기본 문법 — 왜 두 개의 인자를 받는가
왜 "날짜 + 기간" 구조인가?

ADDDATE(날짜, 기간) 형태로 항상 두 개의 값을 받는다. 첫 번째는 계산의 출발점이 되는 날짜, 두 번째는 거기에 더할 양이다. 이 구조가 중요한 이유는 "1월 1일에서 31일을 더한다"는 계산이 단순히 숫자를 더하는 것이 아니라 달력 규칙(월별 일수, 윤년 등)을 따라야 하기 때문이다. MySQL은 이 규칙을 함수 내부에서 대신 처리해준다.

즉 개발자가 직접 "이번 달은 31일까지인지 28일까지인지"를 계산할 필요가 없다. 그 계산을 함수에 위임하는 것이 ADDDATE를 쓰는 핵심 이유다.

이렇게 계산 규칙을 함수 안으로 감추면, 애플리케이션 코드(PHP·자바 등)에서 날짜 라이브러리를 따로 불러오지 않고도 DB 레벨에서 바로 정확한 날짜를 얻을 수 있다는 실무적 이점도 있다.

※ 첫 번째 인자 자리에는 문자열 '2025-01-01'뿐 아니라 CURDATE(), 다른 컬럼(예: created_at)도 그대로 들어갈 수 있다.

두 번째 인자를 "기간"이라고 부르는 것도 의도적이다. 단순한 숫자가 아니라 INTERVAL이라는 별도의 표현식 문법을 쓰기 때문에, 이 함수는 "숫자 하나를 더하는 계산기"가 아니라 "날짜 전용 계산 규칙을 아는 함수"로 설계되어 있다.


2 SUBDATE 기본 문법 — ADDDATE의 거울상
왜 별도 함수로 분리했는가?

SUBDATE(날짜, 기간)는 ADDDATE와 정확히 반대 방향으로 계산한다. 사실 ADDDATE(date, INTERVAL -31 DAY)처럼 음수를 넣으면 SUBDATE와 같은 결과를 얻을 수 있지만, 실무 코드에서는 "빼기"라는 의도를 명시적으로 드러내는 것이 가독성 면에서 훨씬 유리하다. 코드를 읽는 사람이 부호를 뒤집어 계산할 필요 없이 함수 이름만 보고 방향을 알 수 있다.

SUBDATE('2025-01-01', INTERVAL 31 DAY) -- ADDDATE('2025-01-01', INTERVAL -31 DAY) 와 동일한 결과

결국 ADDDATE와 SUBDATE 중 무엇을 쓰느냐는 계산 결과에는 영향이 없고, 코드의 의도를 얼마나 분명하게 전달하느냐의 문제다. 협업 환경에서는 이런 작은 명시성의 차이가 리뷰 시간을 크게 줄여준다.


3 INTERVAL 표현식 — 왜 숫자만 넘기지 않는가
단위 없이는 "31"이 무엇인지 알 수 없다

만약 ADDDATE(date, 31)처럼 숫자만 넘긴다면 MySQL은 그 31을 "일(day)"로 해석한다. 하지만 사람이 "31 뒤"라고 말할 때는 31일일 수도, 31개월일 수도, 31시간일 수도 있다. INTERVAL 31 DAY, INTERVAL 1 MONTH처럼 단위를 명시하면 이 모호함이 완전히 사라진다. 즉 INTERVAL은 "이 숫자의 단위가 무엇인지"를 SQL 문 안에 고정하는 장치다.

이 표현식은 INTERVAL 숫자 단위 순서를 반드시 지켜야 하며, 단위는 예약어이므로 따옴표 없이 그대로 쓴다.

※ 숫자 자리에는 음수도 들어갈 수 있다. INTERVAL -7 DAY는 "7일 전"과 같은 의미이며, 이 성질 덕분에 ADDDATE 하나만으로도 SUBDATE와 같은 계산을 표현할 수 있다.

 

반응형

 

4 지원되는 기간 단위 — 무엇을 고를 수 있는가
DAY와 MONTH만 있는 게 아니다

실무에서 자주 쓰는 단위만 추려도 아래와 같다. 단위를 잘못 고르면 "하루 뒤"를 구하려다 "한 시간 뒤"가 나오는 실수로 이어지므로, 표를 보고 의도한 단위인지 꼭 확인하는 습관이 필요하다.

단위 의미 예시
DAY 일 단위 INTERVAL 7 DAY → 7일 뒤
WEEK 주 단위 (7일과 동일 계산) INTERVAL 2 WEEK → 14일 뒤
MONTH 월 단위, 같은 "일"을 다음 달로 이동 INTERVAL 1 MONTH
YEAR 연 단위 INTERVAL 1 YEAR
HOUR / MINUTE / SECOND 시간 단위, 결과가 DATETIME으로 승격 INTERVAL 3 HOUR

5 ADDDATE의 또 다른 형태 — 정수 일수만 넘기기
왜 이 형태가 존재하는가?

ADDDATE는 ADDDATE(date, days)처럼 INTERVAL 없이 정수만 넘기는 축약형도 지원한다. 이때 days는 항상 "일(day)"로 해석된다. 짧게 쓸 수 있어 편리하지만, 단위가 코드에 드러나지 않아 나중에 코드를 읽는 사람이 "이 숫자가 일인지 다른 단위인지" 헷갈릴 수 있다는 단점이 있다.

ADDDATE('2025-01-01', 31) -- ADDDATE('2025-01-01', INTERVAL 31 DAY) 와 완전히 동일
※ SUBDATE도 동일한 축약형 SUBDATE(date, days)를 지원하지만, 팀 코드 컨벤션에서는 의미를 명확히 하기 위해 INTERVAL 표기를 권장하는 경우가 많다.

결국 두 형태 중 어느 쪽이 "더 옳다"는 없다. 짧게 쓰고 싶다면 축약형을, 여러 사람이 함께 읽는 코드라면 INTERVAL 표기를 선택하면 된다.


6 DATE_ADD / DATE_SUB와의 관계 — 왜 함수가 두 쌍인가
사실상 같은 함수, 이름만 다르다

MySQL에는 ADDDATE·SUBDATE 외에 DATE_ADD·DATE_SUB라는 이름의 함수도 존재하는데, 이 둘은 동작이 완전히 동일하다. ADDDATE/SUBDATE는 표준 SQL 호환을 위한 별칭(alias)이고, DATE_ADD/DATE_SUB는 MySQL이 먼저 제공하던 이름이다. 어느 쪽을 써도 결과는 같으므로, 팀 컨벤션이나 개인 취향에 맞춰 하나를 골라 일관되게 쓰면 된다.

ADDDATE 계열
ADDDATE(date, expr)
표준 SQL 문법과 이름이 비슷해 익숙함
DATE_ADD 계열
DATE_ADD(date, expr)
MySQL 공식 문서 예제에 더 자주 등장

7 월말 날짜의 함정 — 왜 "한 달 뒤"가 항상 같은 일자는 아닌가
1월 31일 + 1개월은 2월 31일이 아니다

MONTH 단위 계산에서 자주 놓치는 부분이다. 예를 들어 ADDDATE('2025-01-31', INTERVAL 1 MONTH)를 계산하면 2월에는 31일이 없으므로 MySQL은 결과를 2월의 마지막 날인 2025-02-28로 자동 보정한다. 이는 버그가 아니라 달력상 존재하지 않는 날짜를 만들지 않기 위한 의도된 동작이다.

이 특성을 모르고 "매달 같은 일자에 알림을 보낸다"는 로직을 짜면, 31일에 가입한 회원의 알림일이 짝수 달마다 슬쩍 당겨지는 현상이 발생할 수 있다. 날짜 계산 로직을 만들 때는 이런 월말 보정을 반드시 염두에 둬야 한다.

※ 1월 31일 + 1개월 = 2월 28일(평년) 또는 2월 29일(윤년). "정확히 30일 뒤"가 필요하다면 MONTH 대신 DAY 단위를 쓰는 것이 더 예측 가능하다.
기준일 +1 MONTH 결과 비고
2025-01-31 2025-02-28 2월은 28일까지라 마지막 날로 보정
2024-01-31 2024-02-29 윤년이라 29일까지 보정
2025-01-15 2025-02-15 대상 달에 15일이 존재하므로 그대로 이동

8 실전 활용 — 만료일·기준일 계산 패턴
왜 CURDATE()와 함께 쓰는 경우가 많은가?

실무에서는 고정된 문자열 날짜보다 "오늘"을 기준으로 계산하는 경우가 압도적으로 많다. 회원가입 즉시 발급되는 쿠폰의 만료일, 최근 N일 데이터 조회, 지난달 매출 집계 등이 대표적이다. 아래 단계는 이런 패턴이 실제로 만들어지는 흐름을 보여준다.

1
기준일 정하기오늘 발급이면 CURDATE(), 특정 이벤트 기준이면 해당 컬럼(예: created_at)
2
기간과 방향 정하기만료는 미래 → ADDDATE, 조회 시작점은 과거 → SUBDATE
3
단위 명시하기일 단위인지 월 단위인지 INTERVAL 뒤에 정확히 적기
4
월말 보정 여부 확인MONTH·YEAR 단위라면 7번 섹션의 보정 규칙을 다시 확인
-- 오늘 기준 30일 뒤를 쿠폰 만료일로 SELECT ADDDATE(CURDATE(), INTERVAL 30 DAY) AS expire_date; -- 최근 7일 데이터만 조회 SELECT * FROM orders WHERE order_date >= SUBDATE(CURDATE(), INTERVAL 7 DAY);
CURDATE()
ADDDATE(..., INTERVAL 30 DAY)
expire_date 컬럼 저장
실전 팁
  • 비교용 조건절(WHERE)에서는 계산 결과가 상수라 인덱스를 그대로 탈 수 있어 성능에 유리하다.
  • 컬럼에 함수를 씌우는 것(예: ADDDATE(order_date, ...) = ...)은 인덱스를 못 타므로 피한다.
  • 기준일 자리에 다른 테이블의 컬럼을 넣어도 동일하게 동작한다.

9 자주 하는 실수
실수 증상 해결
INTERVAL 없이 문자열로 기간을 씀 (예: ADDDATE(date, '1 MONTH')) 문법 오류 또는 의도와 다른 결과 INTERVAL 숫자 단위 형태를 정확히 지켜 작성
단위를 DAY로 착각하고 MONTH를 씀 기대보다 훨씬 먼 미래·과거 날짜가 나옴 4번 섹션의 단위 표를 보고 의도한 단위인지 재확인
31일에 매달 반복되는 이벤트를 MONTH 단위로 계산 2월 등 짧은 달에서 일자가 당겨짐(28·29일) 정확한 일수가 필요하면 DAY 단위로 계산하거나 LAST_DAY 등으로 별도 보정
WHERE절 컬럼에 ADDDATE를 씌워 비교 인덱스를 타지 못해 쿼리가 느려짐 계산은 기준값(우변) 쪽에서만 하고 컬럼은 그대로 비교
ADDDATE와 SUBDATE의 부호 방향을 혼동 미래를 구해야 하는데 과거 날짜가 나옴 더할 땐 ADDDATE, 뺄 땐 SUBDATE로 함수 이름 자체로 방향을 고정
NOW()를 CURDATE() 대신 사용 결과에 시:분:초까지 붙어 날짜만 비교할 때 예상과 다르게 동작 순수 날짜 계산이 목적이면 CURDATE()를 사용

핵심 한 줄 요약

ADDDATE(date, INTERVAL n 단위)기준 날짜에서 n만큼 미래로 이동한 날짜 반환
SUBDATE(date, INTERVAL n 단위)기준 날짜에서 n만큼 과거로 이동한 날짜 반환
DATE_ADD / DATE_SUBADDDATE / SUBDATE와 완전히 같은 동작을 하는 별칭 함수
MONTH·YEAR 단위 주의달력에 없는 날짜는 그 달의 마지막 날로 자동 보정됨
CURDATE()와 조합만료일·최근 N일 조회 같은 실전 계산의 기본 패턴
WHERE절 함수 사용 주의컬럼이 아닌 상수 쪽에서 계산해야 인덱스를 유지할 수 있음

Tags

#MySQL #ADDDATE #SUBDATE #날짜계산 #INTERVAL #날짜함수 #DATE_ADD #DATE_SUB #SQL #데이터베이스 #DB #웹개발 #티스토리
▼ 티스토리 태그 입력란 복사용
MySQL, ADDDATE, SUBDATE, 날짜계산, INTERVAL, 날짜함수, DATE_ADD, DATE_SUB, SQL, 데이터베이스, DB, 웹개발, 티스토리
반응형

댓글