백엔드 개발을 시작하면서 가장 많이 겪는 문제들이 있다.
"로컬에서는 잘 되던 코드가 서버에서는 안 돼요", "팀원이 만든 코드와 합쳤더니 에러가 나요", "매번 수동으로 배포하는 게 너무 번거로워요"...
이런 문제들의 공통점은 무엇일까? 바로 개발과 배포 과정이 수동적이고 환경이 일관되지 않다는 점이다.
CI/CD와 Docker는 이러한 문제들을 해결해주는 현대 개발의 필수 도구이다.
Docker는 추후에 다뤄보는걸로 하고, 이 글에서는 단순 CRUD API 개발에서 시작해서, 왜 이런 기술들이 필요한지,
그리고 실제로 어떻게 적용하는지를 Spring Boot 예시와 함께 살펴볼 예정이다.
CI/CD는 지속적 통합(Continuous Integration)과 지속적 배포(Continuous Deployment)를 뜻한다. 쉽게 말하면 개발자가 코드를 작성하고 배포하는 과정을 자동화하는 것이다.
CI/CD 기본 개념
- CI (지속적 통합)는 여러 개발자가 작성한 코드를 자주 합치고, 자동으로 테스트해서 문제가 없는지 확인하는 과정이다. 예를 들어 팀원 A가 로그인 기능을, 팀원 B가 회원가입 기능을 만들었을 때, 이 둘을 합쳤을 때도 잘 작동하는지 자동으로 검사한다.
- CD (지속적 배포)는 테스트를 통과한 코드를 자동으로 실제 서비스에 배포하는 과정이다. 수동으로 파일을 복사하고 서버를 다시 시작하는 번거로운 작업을 자동화한다.
CI/CD 목표
개발자가 코드만 작성하면, 테스트부터 배포까지 모든 과정이 자동으로 안전하게 처리되도록 하는 것이다.
프론트엔드 vs 백엔드 CI/CD 비교
프론트엔드 CI/CD는 주로 이런 상황에서 사용한다.
- React, Vue 등으로 만든 웹앱을 빌드해서 정적 파일로 만든다
- AWS S3, Netlify, Vercel 같은 호스팅 서비스에 자동 배포한다
- 코드 변경 시 자동으로 미리보기 환경을 생성한다
- 이미지 최적화, CSS/JS 압축 등 성능 최적화 작업을 수행한다
백엔드 CI/CD는 이런 용도로 많이 사용한다.
- API 서버 코드를 테스트하고 서버에 배포한다
- 데이터베이스 마이그레이션을 자동 실행한다
- 여러 서버에 동시 배포한다 (로드밸런싱)
- 서버 재시작이나 무중단 배포를 처리한다
근데 여기서, CRUD 작업만 해본 나로서는 백엔드 ci/cd 과정이 구체적으로 궁금해졌다.
왜냐면 최근에 백엔드에 관심이 생겼는데, 다음 프로젝트때 백엔드를 맡아 ci/cd로 배포자동화를 한 번 해보고 싶기 때문이다.
지금까지 했던 방식 vs CI/CD
지금까지 했던 방식:
- 로컬에서 CRUD API 개발한다 (POST /users, GET /users 등)
- 포스트맨으로 테스트한다
- 서버에 jar 파일을 직접 복사한다
- 서버에서 java -jar app.jar 실행한다
- 뭔가 안되면 다시 1번부터 반복한다
CI/CD를 사용하면:
- 로컬에서 CRUD API 개발한다
- GitHub에 push만 한다
- 끝! 나머지는 자동으로 된다
실제 Spring Boot CRUD 개발 상황으로 예시
예를 들어 회원가입 API를 만들었다고 하자.
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody UserDto userDto) {
User user = userService.createUser(userDto);
return ResponseEntity.ok(user);
}
}
CI 단계에서 자동으로 이런 일이 일어난다:
- ./gradlew build 또는 mvn compile 실행해서 컴파일 에러 체크
- ./gradlew test 실행해서 JUnit 테스트 돌린다: "POST /api/users에 데이터 보내면 DB에 잘 저장되나?"
- H2 테스트 DB로 실제 CRUD 동작 테스트
- 다른 팀원 코드와 합쳤을 때 빌드 에러 없는지 확인
CD 단계에서 자동으로 이런 일이 일어난다:
- ./gradlew bootJar 실행해서 jar 파일 생성
- 서버에 jar 파일 배포
- application.yml의 DB 설정으로 실제 MySQL/PostgreSQL 연결
- Flyway나 Liquibase로 DB 스키마 변경사항 적용
- 기존 Spring Boot 앱 종료하고 새 jar로 재시작
- /actuator/health 엔드포인트로 앱이 정상 실행되는지 확인 (이때 /actuator/health 는 Spring Boot Actuator에서 제공하는 기본 엔드포인트)
왜 필요한가?
Spring Boot CRUD 개발할 때도 이런 상황이 생긴다.
- "어? 로컬에서는 잘 됐는데 서버에서는 DB 연결 에러가 나네?"
- "새로운 Entity 필드 추가했는데 서버 DB 테이블에 컬럼 추가 안했네.."
- "실수로 @Transactional 빠뜨린 코드 올려서 데이터 꼬였네.."
CI/CD는 이런 실수들을 미리 잡아주고, 배포 과정을 자동화해서 실수를 줄여준다. 특히 팀으로 개발할 때 "내가 만든 User Entity가 다른 사람이 만든 Order Entity와 연관관계 잘 맺어지나?" 같은 것들을 자동으로 확인해준다.
마치며
지금까지 살펴본 CI/CD와 Docker는 현대 백엔드 개발의 필수 요소이다.
단순한 CRUD 개발을 넘어서 "안정적이고 효율적인 개발 프로세스"를 만들어주는 도구들이다.
가장 중요한 것은 완벽한 시스템을 한 번에 구축하려 하지 말고, 작은 것부터 시작해서 점진적으로 개선해나가는 것이라고 생각한다.
한 번 설정하는 수고로움보다 얻는 편리함이 훨씬 크니, 다음 프로젝트에서 백엔드를 맡게된다면 꼭 도입해보고 싶다.