[스프링 마이크로서비스 코딩 공작소] 교재를 통해 마이크로서비스를 처음 학습하게 되었다.

 

최근 팀 프로젝트에서 마이크로서비스 구조를 적용해보자는 논의가 있었지만, 프로젝트 일정이 지연되면서 그 아이디어는 자연히 사라지게 되었다. 그렇지만 마이크로서비스를 알지 못한 채 개발자로 성장하는 것은 원치 않아 학습을 시작하게 되었다.

 

학습을 시작하면서 크게 두 가지를 느꼈다. 첫째, 마이크로서비스를 너무 가볍게 생각했다는 것이다. 이전 프로젝트에서도 MSA 도입을 논의했지만, 단순히 서비스를 분리하고 통신하는 수준에서 그쳤던 것이다. 마이크로서비스 환경은 서비스 간 복잡한 통신으로 인해 예상보다 더 큰 복잡도를 동반하며, 이를 해결하기 위한 노력이 필요하다는 것을 깨달았다.

둘째, 마이크로서비스를 위해 이미 많은 기술들이 준비되어 있다는 것이다. 특히 Spring Cloud는 config server, gateway server 등 마이크로서비스에 필요한 다양한 기술들을 쉽게 제공하고 있어, 내가 그동안 경험하지 못한 부분이 많음을 느꼈다.

 

이번 글에서는 [스프링 마이크로서비스 코딩 공작소] 교재에서 다룬 마이크로서비스 환경의 기본 기술들이 각각 어떤 역할을 하는지 간단히 정리해보도록 하겠다.

 

* 첨언하자면 이번 교재에서는 Spring Cloud를 중심으로 다루었지만, 2025년 기준으로는 Spring Cloud와 K8S를 함께 활용하는 프로젝트가 많다고 한다. K8S를 함께 활용할 경우 이번 글에서 다루는 일부 기술을 K8S 쪽 기술로 대체할 수 있다.해당 내용은 별도로 다룰 예정이다.



Spring Cloud

Spring Cloud는 마이크로서비스 환경을 쉽게 구축하고 관리할 수 있도록 다양한 도구와 기능을 제공하는 프레임워크다.

마이크로서비스 간의 통신, 서비스 디스커버리, 보안, 로드 밸런싱 등 MSA의 필수 요소들을 쉽게 구현할 수 있게 해준다.

Spring Cloud는 Config Server, Eureka Server, Gateway Server 등의 기능을 제공하여 서비스 간의 연결과 데이터 교환을 쉽게 할 수 있도록 도와준다. 이를 통해 서비스 간의 결합도를 낮추고, 시스템의 확장성을 확보할 수 있다.

 

Config Server

Config Server는 중앙 집중식 설정 관리 서버로, Spring Cloud Config를 활용해 각 마이크로서비스의 설정을 중앙에서 관리할 수 있다. 각 서비스가 개별적으로 application.properties 또는 application.yml 파일을 작성하는 대신, 모든 설정을 Config Server에 작성하고 각 서비스가 이를 가져오는 방식이다. 또한, 설정 변경 사항을 실시간으로 반영할 수 있어 서비스 간 일관된 구성을 유지하는 데 도움이 된다. 보통 Git 저장소와 연동하여 설정 파일을 관리하며, 이를 통해 설정 관리의 효율성과 일관성을 높일 수 있다.

 

아래와 같이 설정을 통해 Config Server는 Git 저장소에서 설정 파일을 가져와 관리할 수 있다.

server:
  port: 8888
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/my-repo/config-repo

 

Gateway Server

Spring Cloud Gateway는 API Gateway 역할을 수행하는 서버다.

API Gateway는 클라이언트의 요청을 적절한 마이크로서비스로 라우팅하는 중앙 진입점 역할을 하며, 인증/인가, 로드 밸런싱, 로깅, 트래픽 제어 등 다양한 기능을 제공한다.

 

API Gateway를 사용하면 여러 마이크로서비스가 각기 다른 포트를 사용할 때 클라이언트는 하나의 단일 진입점을 통해 모든 요청을 처리할 수 있다. 즉, 여러 서비스가 있을 때 service1.com과 service2.com으로 개별적으로 접근하는 것이 아니라, gateway.com/service1과 gateway.com/service2로 접근하게 되어 gateway.com이라는 단일 진입점을 제공한다. 이를 통해 API Gateway는 서비스 간의 결합도를 낮추고, 다양한 부가 기능을 중앙에서 관리할 수 있도록 도와준다.

 

아래 설정은 /users/** 경로의 요청을 user-service로 전달하도록 하는 설정을 보여준다.

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:8081
          predicates:
            - Path=/users/**

 

Eureka Server

Eureka Server는 서비스 디스커버리를 위한 서버다.

서비스 디스커버리란, 마이크로서비스들이 서로의 위치를 동적으로 찾을 수 있도록 지원하는 메커니즘을 말한다. Eureka 서버에 각 마이크로서비스들이 등록되면, 다른 서비스들은 Eureka를 통해 필요한 서비스의 위치를 조회할 수 있다. 이를 통해 부하 분산, 장애 대응, 동적 서비스 인스턴스 관리 등을 보다 쉽게 구현할 수 있다.

 

서비스 디스커버리는 MSA에서 중요한 요소로, 서비스 간의 통신을 효율적으로 처리하고, 서비스가 추가되거나 제거될 때도 다른 서비스에 미치는 영향을 최소화할 수 있다.

 

아래처럼 Eureka Server를 설정하고, 다른 마이크로서비스들이 이를 사용하도록 할 수 있다.

server:
  port: 8761
spring:
  application:
    name: eureka-server
  eureka:
    client:
      register-with-eureka: false
      fetch-registry: false

 

Kafka / Zookeeper

Kafka는 대량의 데이터를 빠르게 처리하고, 서비스 간의 비동기 이벤트를 전달하는 메시지 브로커다.

Kafka는 MSA 환경에서 서비스 간의 비동기적이고 효율적인 통신을 위해 사용된다. 각 서비스는 Kafka를 통해 이벤트를 발행하고, 다른 서비스들은 이를 소비하여 처리할 수 있다. 이를 통해 서비스 간의 결합도를 낮추고, 시스템의 확장성을 높일 수 있다.

 

또한 Kafka는 로그, 이벤트 스트림, 데이터 파이프라인 등 다양한 용도로 활용된다. 예를 들어, Kafka는 마이크로서비스들 간의 실시간 이벤트를 처리하거나, 데이터 흐름을 분리하여 처리하는 데 적합하다.

Zookeeper는 Kafka의 클러스터 노드 관리 및 리더 선출 등의 역할을 수행하는 코디네이션 서비스다. Kafka와 함께 사용하면 클러스터 관리가 용이해지고, 서비스의 장애 대응 및 복구가 간편해진다.

 

아래는 my-topic라는 kafka topic에 메시지를 발행하는 java 코드 예시이다.

@KafkaTemplate("my-topic")
public void sendMessage(String message) {
    kafkaTemplate.send("my-topic", message);
}

 

Keycloak

Keycloak은 오픈소스 인증 및 권한 부여 솔루션으로, OAuth 2.0, OpenID Connect, JWT 등의 표준을 지원한다. MSA 환경에서는 각 서비스가 독립적으로 보안 인증을 처리하는 대신, Keycloak을 통해 중앙에서 모든 인증과 권한 부여를 관리할 수 있다.

 

Keycloak을 활용하면 사용자 로그인, 액세스 토큰 발급 등을 쉽게 처리할 수 있다. 또한, Keycloak은 사용자 인증뿐만 아니라, 역할 기반 접근 제어(RBAC)와 같은 기능을 제공하여 서비스 간의 보안을 효과적으로 관리할 수 있다.

 

ELK 스택

ELK 스택은 Elasticsearch, Logstash, Kibana의 세 가지 오픈소스 도구로 구성된 시스템으로, 대량의 로그와 데이터를 수집, 처리, 검색, 분석 및 시각화하는 데 유용하다. 마이크로서비스 아키텍처(MSA)에서 각 서비스가 로그를 별도로 처리하고 관리하는 것은 매우 복잡할 수 있기 때문에, ELK 스택을 활용하면 모든 로그를 중앙에서 수집하고 분석할 수 있어 효율적인 모니터링과 디버깅을 할 수 있다.

 

Elasticsearch
Elasticsearch는 분산형 검색 엔진으로, 데이터를 빠르게 검색하고 분석할 수 있다. 로그 데이터를 저장하고, 인덱싱하여 검색 성능을 극대화한다. Elasticsearch는 RESTful API를 통해 다른 시스템과 쉽게 통합될 수 있어 MSA 환경에서 로그 데이터를 빠르게 검색하고 실시간으로 분석할 수 있는 강력한 도구로 사용된다.

예를 들어, 서비스 로그 데이터를 Elasticsearch에 저장한 후, Kibana를 사용해 시각적으로 대시보드를 구성하거나, 로그를 검색해 문제를 해결할 수 있다.


Logstash
Logstash는 데이터를 수집, 변환하고 저장소로 전송하는 데이터 파이프라인 도구다. 여러 다양한 소스(파일, DB, 메시지 큐 등)로부터 데이터를 수집하여 변환하고 Elasticsearch로 전송한다. Logstash는 로그 데이터를 수집하면서 로그의 형식을 변환하거나, 데이터를 필터링하고, 정규화하는 작업을 처리한다. 예를 들어, 다양한 포맷의 로그를 하나의 일관된 포맷으로 변환하고, 불필요한 로그를 제외하는 등의 처리가 가능하다.

 

Kibana
Kibana는 Elasticsearch 데이터를 시각화하는 도구로, 로그 데이터를 대시보드 형태로 시각화하여 직관적으로 모니터링할 수 있도록 돕는다. Kibana를 사용하면 실시간으로 서비스의 상태를 모니터링하고, 로그를 쉽게 검색하고 분석할 수 있다. 대시보드에서 데이터를 그래프나 차트로 시각화하여 문제가 발생한 지점을 빠르게 찾아낼 수 있다.

 

Zipkin

Zipkin은 분산 트레이싱 시스템으로, 마이크로서비스 간의 요청 흐름을 추적하여 성능 병목 현상 및 지연 문제를 분석하는 데 사용된다. Zipkin을 활용하면 서비스 간의 요청이 어떤 경로를 통해 전달되는지, 어느 지점에서 문제가 발생하는지 명확하게 파악할 수 있다.

 

Resilience4j

Resilience4j는 마이크로서비스의 장애 대응을 위한 라이브러리다. 마이크로서비스는 서로 의존성이 있기 때문에, 한 서비스의 장애가 다른 서비스로 전파될 수 있다. Resilience4j는 Circuit Breaker, Retry, Rate Limiter, Timeout 등 다양한 패턴을 통해 이러한 장애를 예방하고 복구하는 데 도움을 주며, 마이크로서비스의 신뢰성을 높여준다.

 

대표적으로 Circuit Breaker 패턴만 다뤄보도록 하겠다.

 

Circuit Breaker 패턴

Circuit Breaker(서킷 브레이커) 패턴은 특정 서비스가 장애를 겪을 경우, 다른 서비스로 영향을 전파하지 않도록 차단하는 기법이다. Resilience4j와 같은 라이브러리를 활용하여 이 패턴을 구현할 수 있다. 서킷 브레이커는 서비스가 실패한 경우, 잠시 동안 해당 서비스를 호출하지 않도록 하여, 시스템이 복구될 수 있는 시간을 준다.

 

아래 코드는 Circuit Breaker 예시로, getData() 실행 중 오류 발생 시 별도 작성된 fallbackMethod()를 수행하도록 한다.

@CircuitBreaker(name = "backendA", fallbackMethod = "fallbackMethod")
public String getData() {
    // 외부 API 호출
}

public String fallbackMethod(Exception e) {
    return "서비스가 현재 이용할 수 없습니다.";
}

 

끝!

 

Fast Refresh 무한루프

Next JS 사용 중 위와 같은 현상을 접했다.

클라이언트 컴포넌트에서 api 경로에 접근해서 데이터를 가져오는 테스트를 하던 중이었는데, 화면은 이상 없이 로딩되었지만 콘솔을 보니 저렇게 Fast Refresh라는 로그가 계속 반복되고 있었다.

 

Fast Refresh는 웹소켓이 연결을 유지하는 것처럼 특정한 상황이라면 정상적일 수도 있으나, 이번 경우는 단순히 1회성으로 데이터를 가져오는 화면이었고 refresh 속도도 매우 빨랐기에 불필요한 현상이었다.

당장은 문제가 없을지라도 향후 리렌더링이 무한으로 발생하는 등 이슈가 될 수 있기에 코드를 검토해보았다.

 

'use client';

import { useEffect, useState } from 'react';

export default function Fetch() {
  const [user, setUser] = useState({id: null});
  useEffect(() => {
    fetch(process.env.NEXT_PUBLIC_API_URL + '/api/1')
      .then(type => type.json())
      .then(result => {
        setUser(result);
      })
  });

  return (
    <>
      <h1>/app/sub/fetch/page.js</h1>
      <p>{user.id}</p>
      <a href="/">/app/page.js</a>
    </>
  )
}

 

검토 결과, useEffect 내부에서 setUser를 사용한 것이 무한루프의 원인이었다.

useEffect 내부에서 setUser를 실행하면서 상태가 변경되게 되는데, 상태 변경에 따라 useEffect가 재실행되면서 무한 루프가 발생했다.

useEffect() 실행 set User 사용
컴포넌트 리렌더링 user 상태 변경

(시계방향으로 순회)

 

그래서 useEffect에 의존성 배열을 추가함으로써 무한루프 현상을 해결하였다.

의존성 배열을 추가하게 되면 컴포넌트가 처음 렌더링될 때에만 useEffect가 실행되고 되고, 이후에는 상태가 변경되더라도 실행되지 않아 무한루프를 방지할 수 있다.

  useEffect(() => {
    fetch(process.env.NEXT_PUBLIC_API_URL + '/api/1')
      .then(type => type.json())
      .then(result => {
        setUser(result);
      })
  }, []);

 

* 2025.03.14 추가

의존성 배열(Dependency Array)이란, useEffect가 언제 실행될지를 결정하는 배열이다.

예를 들어 user 상태가 변경될 때마다 useEffect를 실행하고 싶다면, useEffect의 두 번째 인자로 [user]를 전달하면 된다.

 

의존성 배열이 없는 경우 (useEffect(() => {...}))
→ 모든 렌더링마다 실행되므로 성능 이슈가 발생할 수 있다.

 

빈 배열 []을 전달하는 경우 (useEffect(() => {...}, []))
→ 처음 렌더링(마운트) 시에만 실행되고, 이후에는 다시 실행되지 않는다.

 

 

끝!

You have a Server Component that imports next/router.

 

Next JS 학습 중 위 에러가 발생했다.

id라는 이름의 path parameter를 사용하려던 중 발생했는데, 그 해결과정을 간략히 정리해보고자 한다.

 



1. 에러 발생 및 설명

먼저, 문제가 된 코드는 아래와 같다.

import { useRouter } from 'next/router';

export default function Id() {
  const router = useRouter();
  const id = Number(router.query.id);

  return (
    <>
      <h1>/pages/sub/[id]/page.js</h1>
      <p>Parameter id: {id}</p>
      <a href="/">/pages/page.js</a>
    </>
  );
}

/sub/[id] 라는 경로로 접근했을 때, next/router의 useRouter를 활용하여 query 값에서 id를 찾아내는 구조이다.

하지만 위 코드를 컴파일하던 중 You have a Server Component that imports next/router, 즉  서버 컴포넌트에서 next/router를 사용했다는 에러가 발생했다.

 

알아보니 next.js 13 버전부터 기존 pages 폴더에서 app 폴더 기반 라우팅 시스템으로 바뀌었는데, 이와 함께 서버 컴포넌트와 클라이언트 컴포넌트를 구분하기 시작했다. next/router의 useRouter는 클라이언트 전용 훅으로서, 클라이언트 컴포넌트에서만 사용이 가능하기 때문에 서버 컴포넌트에서 호출 시 위 에러가 발생한다.


2. 해결

다른 블로그를 참고했을 때 서버 컴포넌트에서는 next/router가 대신 next/navigation을 사용해야 한다고 하여 적용해보았다. 하지만 next/navigation의 useRouter는 query를 포함하고 있지 않아 고민하던 중, 아래와 같이 pathname을 활용하는 방식으로 먼저 접근해보았다.

'use client';

import { usePathname } from 'next/navigation';

export default function IdPage() {
  const pathname = usePathname();
  const id = pathname.split("/")[2];

  return (
    <div>
      <h1>/pages/sub/[id]/page.js</h1>
       <p>Parameter id: {id}</p>
       <a href="/">/pages/page.js</a>
    </div>
  );
}

접근경로인 /sub/[id] 을 pathname으로 받은 후 split()하여 id를 추출하는 방식이다.

 

하지만 위와 같은 방식은 url이 변경될 때마다 코드의 수정이 필요하다.

그래서 좀 더 알아보니, 간단한 해결책이 나왔다. 알고보니 app 폴더 기반 라우팅 시스템에서는 params 객체를 통해 path parameter 값을 받아올 수 있었다.

따라 아래와 같이 params 객체에서 id를 추출하는 방식을 채택했다.

export default function Id({ params }) {
  const { id } = params;

  return (
    <>
      <h1>/pages/sub/[id]/page.js</h1>
      <p>Parameter id: {id}</p>
      <a href="/">/pages/page.js</a>
    </>
  );
}

3. 서버 컴포넌트 & 클라이언트 컴포넌트

서버 컴포넌트

서버 컴포넌트는 서버에서만 실행되는 컴포넌트로 클라이언트 측에서 렌더링되는 JavaScript 코드를 보내지 않고, 서버에서 데이터를 처리하거나 렌더링을 한 뒤 HTML을 클라이언트로 전송한다. Next JS에서는 별다른 선언을 하지 않으면 서버 컴포넌트로 간주한다.

  • 서버에서 동적으로 데이터를 처리하고 렌더링
  • 클라이언트에서 JavaScript를 실행하지 않으며 필요한 경우 React의 상태 업데이트나 이벤트 핸들러 없이 서버에서 완료된 HTML을 클라이언트로 전송
  • 서버 컴포넌트는 클라이언트 사이드 라이브러리나 API 호출과 같은 동적 JavaScript 동작을 할 수 없으며, 대신 서버에서 데이터를 가져와서 HTML을 렌더링

 

클라이언트 컴포넌트

클라이언트 컴포넌트는 클라이언트 브라우저에서 실행되는 컴포넌트이며, 상태 관리, 이벤트 핸들링, 클라이언트 측 API 요청, 동적 렌더링 등을 클라이언트에서 처리할 수 있다. 최상단에 'use client'를 적음으로써 클라이언트 컴포넌트를 선언할 수 있다.

  • 클라이언트에서 JavaScript를 실행하여 동적인 콘텐츠를 표시
  • 상태(state)를 관리하거나 이벤트 핸들러를 통해 동작을 처리
  • 클라이언트에서 useState, useEffect, useRouter와 같은 React 훅을 사용하여 UI를 동적으로 업데이트

 

서버 컴포넌트와 클라이언트 컴포넌트의 구분 이유

  • 서버 컴포넌트는 최적화된 서버 사이드 렌더링(SSR)을 통해 페이지를 클라이언트에게 전달하고, 불필요한 JavaScript 번들을 줄여 페이지 로딩 속도를 개선
  • 클라이언트 컴포넌트는 상태 관리, 이벤트 처리, 동적 UI 업데이트를 클라이언트에서 처리하게 함으로써 사용자 경험 향상

끝!

'Languages > Next.js' 카테고리의 다른 글

[NextJS] Fast Refresh 무한루프 해결  (0) 2025.02.18

맥이나 유닉스 사용자라면 따로 설치하지 않아도 gcc 컴파일이 가능하지만, 일반사용자를 대상으로 한 Windows의 경우에는 별도로 gcc를 설치해야 C 컴파일이 가능하다.

이 때 MinGW를 사용할 수 있다.

 

gcc (GNU C Compiler) : 리눅스 계열 운영체제의 표준적인 C 컴파일

MinGW (Minimalist GNU For Windows) : API를 이용하여 Windows에서 gcc를 사용할 수 있게 하는 프로그램



1. MinGW 설치

설치파일은 SourceForge 페이지에서 받을 수 있다.

https://sourceforge.net/projects/mingw-w64/files/

 

MinGW-w64 - for 32 and 64 bit Windows - Browse Files at SourceForge.net

Sendbird's chat, voice, and video APIs power conversations and communities in hundreds of the most innovative apps and products. Sendbird’s feature-rich platform, and pre-fab UI components make developers more productive. We take care of a ton of operati

sourceforge.net

 

경로로 접근하면 아래와 같이 먼저 Files 탭을 선택해 주고

 

 

이후 밑으로 내려가서 MinGW-W64 GCC-8.1.0 아래에서 설치파일을 받으면 된다.

나는 x86_64-posix-seh 를 선택했다.

잠시 기다리면 다운로드가 진행되는데, 다운이 완료되면 C드라이브 아래에 압축을 해제한다. (C:\mingw64)

만약 64비트가 아니라 32비트를 사용중이라면 아래 i686 항목을 받아야 한다.


2. gcc 확인 및 환경변수 설정

C:\mingw64 경로대로 압축을 해제했다면, gcc가 잘 설치되었는지 확인해보자.

cmd창에 들어가서 다음과 같이 입력했을 때, 정상적으로 설치되었다면 다음과 비슷한 화면이 표출된다.

C:\mingw64\bin\gcc -v

 

매번 경로를 다 적을 수 없으니, 환경변수를 등록한다.

[환경 변수]를 검색해서 [시스템 환경 변수 편집] 화면에 들어간다.

 

이후 [환경변수]를 클릭, Path를 찾아 선택한 후 [편집]을 누른다.

 

[새로 만들기]를 클릭한 후 C:\mingw64\bin 경로를 입력한다.

 

여기까지 완료하면 이제 전체 경로를 입력하지 않고도 gcc만으로 실행이 가능하다.

cmd 창을 새로 열어준 후, gcc -v를 입력해서 다시 확인해보자.

 

* 환경변수를 등록한 후에는 cmd 창을 새로 열어야 적용된다.


3. C 컴파일 테스트

이제 gcc 사용이 가능하니, 마지막으로 C를 통해 Hello World를 찍어보자.

#include <stdio.h>

int main() {
	printf("Hello World!");
}

 

위와 같이 hello.c를 작성한 후, 해당 위치에서 cmd 창으로 gcc hello.c를 입력한다.

그리고 다시 폴더를 보면 a.exe라는 새로운 파일이 생기고, 실행해보면 Hello World!가 콘솔에 출력된다.

 

만약 결과파일 이름을 다르게 지정하고 싶다면, gcc -o hello.exe hello.c 와 같이 옵션을 주면 된다.

 

끝!

 

git add 명령어를 실행하다 보면 아래 경고창을 마주칠 때가 있다.

LF will be replace by CRLF the next time Git touches it

 

찾아보니 Windows 운영체제에서 Git을 사용할 때, core.autocrlf 설정과 관련된 경고라고 한다.



1. LF와 CRLF

경고에 등장하는 LF와 CRLF는 무엇일까.

이들은 줄바꿈문자, 즉 개행문자를 나타내는 것으로 텍스트 파일에서 줄을 구분하는 데에 사용한다.

 

LF (Line Feed)

Unix 및 Unix 기반 시스템에서 사용되는 줄 바꿈 문자이며, 단순히 줄을 다음으로 이동시키는 역할을 한다. (₩n)

ASCII 코드에서는 10진수 값으로 10에 해당한다.

 

 CRLF (Carriage Return + Line Feed)

DOS 및 Windows 시스템에서 사용되는 줄 바꿈 문자로, 줄을 다음으로 이동한 후 커서를 줄의 시작 부분으로 이동시킨다.

ASCII 코드에서 10진수 값으로 13(Carriage Return)과 10(Line Feed)에 해당한다.

 

즉 Unix 계열에서는 LF를, Windows 계열에서는 CRLF를 사용하며, 이로 인해 크로스플랫폼 프로젝트에서 문제가 될 수도 있다고 한다.


2. core.autocrlf 설정

Git에서 제공하는 core.autocrlf 설정은 Git이 작업 디렉토리에서 줄바꿈 문자를 어떻게 다루는지를 제어한다.

아래의 3가지 설정이 가능하다.

 

▶ core.autocrlf=false : Git이 줄바꿈 문자를 변경하지 않음
▶ core.autocrlf=true : 저장소에서 가져올 때 CRLF를 LF로 변환함 / 저장소로 보낼 때는 LF를 CRLF로 변

▶ core.autocrlf=input : LF 사용


3. 해결 방법

위 경고는 OS간 서로 다른 줄바꿈 문자 때문에 Git이 표출한 메시지이며, core.autocrlf 설정을 통해 해결할 수 있다.

 

Windows의 경우 CRLF를 사용하므로, core.autocrlf를 true로 설정한다.

git config --global core.autocrlf true

 

 

Linux, Unix의 경우 LF만을 사용하므로 core.autocrlf를 input로 설정한다.

git config --global core.autocrlf input

 

아니면 아예 false를 줌으로써, core.autocrlf를 해제해 버리는 방법도 가능하다.

git config --global core.autocrlf false

 

끝!


1. 빌드 도구 종류

자바 기반 프로젝트에서의 빌드 도구는 대표적으로 3가지가 있다.

 

첫째는 Ant 라는 불리는, XML 기반 빌드 스크립트를 사용하는 도구가 있다. build.xml 이라는 빌드 스크립트를 사용하여 소스코드 컴파일, 테스트, 배포 등을 수행했으며, 2000년대 초반, 자바 기술 스택이 확장됨에 따라 많은 기업과 개발자가 사용했다고 한다. 특유의 간단한 구조와 확장성으로 인기가 많았지만, 새로운 빌드 도구가 등장함에 따라 사용이 줄었다.

 

둘째는 Maven이다. pom.xml 이라는 빌드 스크립트를 사용하는데, 종속성 관리의 측면에서 Ant와 큰 차이를 보였다. Ant는 외부 라이브러리의 종속성 관리를 위해 직접 라이브러리를 다운로드하고 클래스패스에 추가를 해주어야 했지만, Maven은 중앙저장소에서 라이브러리를 관리하면서 pom 파일에 종속성을 명시하면 자동으로 라이브러리를 다운로드 및 관리해주는 편리성을 보였다.

 

마지막은 Gradle로, build.gradle이라는 빌드 스크립트를 사용한다. Gradle은 이전 도구들과 달리 kotlin이나 groovy와 같은 DSL (Domain-Specific Language)를 사용하는데, 가독성이 뛰어나고 유연성이 높아 복잡한 빌드 프로세스를 간결하게 표현할 수 있다는 장점을 가진다. 또한 Maven과 호환되는 종속성 관리시스템을 지원하며, 사용자가 유연하게 빌드 프로세스를 정의할 수 있다.


2. Gradle 설치

Gradle 8.7 버전을 기준으로 설치해보자.

https://gradle.org 에서 Install Gradle 8.7 을 클릭한다.

Installation 페이지에서 아래로 내리다 보면 Installing manually 항목이 나오는데, Step1의 Binary-only를 선택해주자.

 

압축파일이 다운될 텐데, 편의를 위해 C드라이브 아래에 압축을 해제한다. (C:\gradle-8.7)

 

 

이제 환경변수 설정이 필요하다.

[환경 변수]를 검색해서 [시스템 환경 변수 편집] 화면에 들어간다.

 

 

이후 [환경변수]를 클릭, Path를 찾아 선택한 후 [편집]을 누른다.

 

 

[새로 만들기]를 클릭한 후에, 앞서 압축을 해제한 gradle-8.7 폴더에서 bin 폴더의 경로를 찾아 입력해준다.

C:\gradle-8.7\bin

그리고 혹시 모르니 jdk 바로 아래까지 위치를 옮겨주면 된다.

 

cmd 창을 새로 열어 gradle -v 를 입력해보면, 정상 설치된 것을 확인할 수 있다.


3. Gradle 사용법

  3.1 gradle init

테스트용 프로젝트 폴더를 만든 후, 해당 경로에서 gradle init 을 실행한다.

그러면 gradle 프로젝트 초기화가 시작되면서 아래 선택사항을 정해야 한다.

Select type of build to generate:
  1: Application
  2: Library
  3: Gradle plugin
  4: Basic (build structure only)
Enter selection (default: Application) [1..4]

  생성할 타입을 묻는 부분으로, 웹어플리케이션 개발자라면 그냥 default로 넘어가면 된다.

Select implementation language:
  1: Java
  2: Kotlin
  3: Groovy
  4: Scala
  5: C++
  6: Swift
Enter selection (default: Java) [1..6]

  대상 언어를 묻는 항목이며 Java가 default이다.

Enter target Java version (min: 7, default: 21):

  Java를 선택할 시 버전을 선택하는 항목이며, default는 현재 LTS 중 가장 버전이 높은 21이다.

Project name (default: test):

  프로젝트 이름을 묻는 항목이며 default는 폴더명과 같다.

Select application structure:
  1: Single application project
  2: Application and library project
Enter selection (default: Single application project) [1..2]

  프로젝트 구조 관련으로, 단일 프로젝트인지 서브 프로젝트를 포함하는지 묻는 항목이다.

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection (default: Kotlin) [1..2]

  Domain-Specific Language를 묻는 항목이며, kotlin이 default이다.

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4]

  테스트 프레임워크를 고르는 항목으로, JUnit 4 이후 버전인 JUnit Jupiter가 default로 설정되어 있다.

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

  새롭게 도입된 API를 사용할지 묻는 항목으로, 안정성을 중시한다면 default인 no로 선택하는 것이 좋다.

 

여기까지 선택을 마치고 나면 Gradle 초기화가 완료된다.


  3.2 폴더 구조

초기화를 마치면 폴더는 아래와 같은 구조를 가지게 된다.

각 항목을 간단히 설명하면 아래와 같다.

app : main 프로젝트

app/src : 소스코드 위치

app/build.gradle : main 프로젝트의 빌드 명세서

gradle/wrapper : Gradle이 설치되지 않은 경우 자동으로 설치해주는 도구

gradle/libs.versions.toml : 의존하는 버전 정보

gradlew : Gradle이 로컬 컴퓨터에 설치되지 않은 경우 자동으로 설치하여 실행하는 도구 (Linux/Unix용)

gradlew.bat : Gradle이 로컬 컴퓨터에 설치되지 않은 경우 자동으로 설치하여 실행하는 도구 (Windows용)

settings.gradle : Gradle 메인 설정

.gitattributes : Git 보조 설정 파일

.gitignore : Git ignore 정보


  3.3 빌드 및 실행

Gradle 초기화를 하고 나면, Hello World를 찍을 수 있는 샘플 데이터가 함께 생긴다.

이를 활용해서 빌드, 그리고 실행하는 것까지 살펴본다.

 

대상 프로젝트 폴더에서 gradle build를 입력한다.

그러면 app 폴더 하위에 build라는 폴더가 생기고, 그 안에 App.class라는 샘플 클래스파일이 생긴다.

 

이제 gradle run 명령을 내리게 되면, 프로젝트가 실행되면서 Hello World가 출력된다.

 

만약 다른 부연설명 없이 결과만 보고 싶다면, gradle -q run 명령을 내리면 된다. (quiet)

 

이후 build 폴더를 다시 없애고 싶다면, gradle clean 명령을 통해 할 수 있다.

 

끝!

+ Recent posts