우선 로그란 컴퓨터 프로그램이 실핸되는 동안 일어나는 사건이나 상태를 기록한 기록부라고 생각하면 된다.

예를 들어, 사람이 일기를 쓰고 나중에 일기장으로 그날 하루를 돌아보듯 프로그램도 "어떤 일이 일어났는지"를 기록한다. 

 

로그 관리는 문제를 빠르게 찾아내거나, 시스템. 상태를 확인할 때 매우 중요하다.

 

Gradle을 이용한 프로젝트 설정 (Gradle로 개발을 하고 있기 때문에 Gradle에 대해서 작성)

더보기
더보기

Gradle에 대해 모르거나 까먹었을 수 있다. 

Gradle은 프로젝트를 빌드(컴파일, 테스트, 배포 등) 하는 도구이다.  그렇다면 또 빌드에 대해서 헷갈릴 수 있다. 빌드란? 소스코드 파일을 컴퓨터에서 실행할 수 있는 독립적인 형태로 변환하는 과정과 결과를 말한다. 즉, 개발자가 작성한 소스코드, 각각의 파일 자원 ( .xml, .jpa, .jpg, .properties)을 jvm이나 톰캣 같은 WAS가 인식할 수 있도록 패키징하는 과정 및 결과물을 빌드라고 한다.

 

다시 돌아와서 Gradle은 스프링 부트와 안드로이드에서 사용되며 빌드 속도가 Maven에 비해 10 ~ 100배 정도 빠르며, Java, C/C++, Python 등을 지원한다.

 

Gradle의 특징으로는 

1. 가독성이 좋다.

2. 재사용에 용이

3. 구조적인 장점

4. 편리함

5. 멀티 프로젝트

등이 있다. 

 

로그 관리를 위해서는 Spring Boot의 로깅 기능(기본적으로 Logback)을 사용한다.

build.gradle 파일에 아래와 같이 의존성을 추가한다.

plugins {
    id("org.springframework.boot") version "2.7.5" // Spring Boot 버전
    id("io.spring.dependency-management") version "1.0.15.RELEASE"
    kotlin("jvm") version "1.6.21"
    kotlin("plugin.spring") version "1.6.21"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
    mavenCentral()
}

dependencies {
    // Spring Boot 기본 기능 (웹, 로깅 등)
    implementation("org.springframework.boot:spring-boot-starter-web")
    
    // Spring Boot AOP, 로깅 관련 기능 (Logback은 기본 포함)
    implementation("org.springframework.boot:spring-boot-starter-aop")
    
    // Kotlin 관련 의존성
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

 

 

로그 저장 : Logback과 파일 관리

Logback과 SLF4J에 대해 먼저 알아보자

  • SLF4J는 여러 로깅 라이브러리(Logback, Log4 J 등)를 추상화하여 사용하는 인터페이스이다.
  • Logback 은 Spring Boot에서 기본적으로 사용하는 로깅 프레임워크이다. Logback은 로그를 파일에 저장하거나 콘솔에 출력하는 등의 기능을 제공한다.

 

로그 설정 파일 : logback-spring.xml

Spring Boot 에서는 src/main/resource 폴더 안에 logbak-spring.xml 파일을 만들면, 로깅의 형식, 저장 위치, 파일 회전(일정 시간마다 새 파일로 저장)등을 설정할 수 있다.

<configuration>
    <!-- FILE appender: 로그를 파일에 저장 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 로그 파일 저장 위치 -->
        <file>logs/app.log</file>
        <!-- 로그 파일 회전 정책: 매일 새 파일 생성하고, 30일간 보관 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 로그 출력 형식: 날짜, 스레드, 로그 레벨, 로거 이름, 메시지 -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 콘솔에도 로그 출력 (옵션) -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 루트 로거 설정: 기본 로그 레벨을 INFO로 설정 -->
    <root level="INFO">
        <appender-ref ref="FILE" />
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

 

위 설정은 

  • logs/app.log 라는 파일에 로그를 기록한다.
  • 매일 새로운 로그 파일을 만들고, 30일 지난 로그는 삭제한다
  • 콘솔에도 로그가 출력되어, 개발 중에 쉽게 확인할 수 있다.

이렇게 이해하면 된다.

 

 

위와 같이 저장한 로그 파일에서 로그를 조회하기 위한 방법은 몇 가지가 있다.

(1) 파일 탐색기나 텍스트 에디터 사용

  • 로그 파일을 텍스트 에디터(VS Code, Notepad++ 등)로 열어 직접 내용을 확인하는 방법

(2) 터미널 명령어 사용

  • tail : 최신 로그 및 몇 줄을 확인할때 사용한다. ( 예 : tail -f app.log )
  • grep : 특정 키워드(ERROR, 특정 메서드 이름)를 검색할 때 사용한다. ( 예 : grep "ERROR" logs/app.log )

 

 

중앙 집중식 로그 관리 시스템 

ELK 스택 : Elasticsearch, Logstash, Kibana 를 사용하면, 웹 인터페이스에서 로그를 필터링 하고 조회할 수 있따.

이 방법은 여러 서버의 로그를 한 곳에서 모아 볼 때 유용하다.

 

ELK 스택의 구성

  • Elasticsearch: 로그 데이터를 저장하고, 빠르게 검색할 수 있는 데이터베이스 역할을 합니다.
  • Logstash/Filebeat: 여러 서버에서 로그 파일을 수집하여 Elasticsearch로 전송합니다.
  • Kibana: Elasticsearch에 저장된 로그 데이터를 시각화하고, 웹 인터페이스에서 검색할 수 있도록 해 줍니다.

ELK 스택의 장점

  • 중앙 집중식 조회: 모든 서버의 로그를 한 곳에서 볼 수 있습니다.
  • 실시간 검색 및 필터링: 원하는 키워드나 시간대에 맞춰 로그를 빠르게 검색할 수 있습니다.
  • 대시보드: 시각화 도구를 사용해 서버 상태를 모니터링하고 문제를 빠르게 파악할 수 있습니다.

 

추후에 중앙 집중식 로그 관리 시스템에 대해서 정리가 필요할 것 같다.

AOP란 무엇일까?

AOP(Aspect-Oriented Programming)는 관점 지향 프로그래밍이라고 부른다. 너무 어려운데 풀어서 설명하면 프로그램의 핵심 로직과 공통 기능(예 : 로깅, 보안, 트랜잭션 관리)을 분리하여 작성할 수 있도록 도와주는 것이라고 보면 된다.

 

즉, 핵심 비즈니스 로직에 영향을 주지 않으면서도 여러 곳에서 반복되는 기능을 한 곳에 모아서 관리할 수 있게 해준다(너무 좋은것 아닌가?)

 

더보기
더보기

더 쉽게 예를 들어 학교에서 학생들이 공부하는 주요 수업(핵심 로직)이 있다고 해보자. 그런데, 시험 감독, 출석 체크, 학부모 연락 등과 같은 공통 작업도 있는데, 이 작업들은 모든 수업에 걸쳐 반복된다. 

AOP는 이처럼 "공통 작업(예 : 시험 감독)" 을 한 곳에서 관리하고, 각 수업(비즈니스 로직)에서는 신경 쓰지 않도록 분리하는 역할을 한다.

 

AOP의 주요 개념

  1.  Aspect (관점) : AOP의 핵심 모듈로, 여러 곳에 적용할 공통 기능을 모아둔 단위이다. 예를 들어, 로깅 Aspect는 애플리케이션 전반에 걸쳐 로그를 기록하는 기능을 한 곳에 모아서 관리한다.
  2.  Join Point (조인 포인트) : Aspect가 적용될 수 있는 지점을 의미한다. 메서드 호출, 예외 발생 등이 Join Point가 될 수 있다.
  3.  Pointcut (포인트컷) : 어떤 Join Poin에 Aspect(Advice)를 적용할 것인지 정하는 조건이다. "어떤 메서드가 호출될 때마다 로그를 남겨라"와 같은 조건을 정의한다.
  4.  Advice (어드바이스) : 실제로 수행되는 작업이다. 
  5.  Weaving (위빙) : Aspect를 실제 코드에 적용하는 과정이다. Srping AOP는 런타임(실행 시)에 동적으로 위빙을 수행하여, 개발자가 별도로 공통 기능 코드를 삽입하지 않아도 자동으로 적용된다.

 

Spring Boot 에서 Spring AOP 설정

별도의 XML 설정 없이, spring-boot-stater-aop 의존성을 추가하기만 하면 AOP 기능을 사용할 수 있다.

build.gradle 예시

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    implementation 'org.springframework.boot:spring-boot-starter'
}

 

 

간단한 로깅 Aspect 예시

메서드 실행 전후에 로그를 남기는 간단한 예시

import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component

@Aspect
@Component
class LoggingAspect {
    private val logger = LoggerFactory.getLogger(this.javaClass)

    // 모든 Service 패키지 내의 모든 메서드 실행 시점에 적용
    @Around("execution(* com.example.service.*.*(..))")
    fun logAround(joinPoint: ProceedingJoinPoint): Any? {
        val methodName = joinPoint.signature.name
        
        // 로그 출력 예시: "메서드 시작: getUserById"
        logger.info("메서드 시작: $methodName")  // 예시 로그: [INFO] 2025-02-26 12:34:56 - 메서드 시작: getUserById
        
        try {
            val result = joinPoint.proceed() // 실제 메서드 실행
            
            // 로그 출력 예시: "메서드 종료: getUserById"
            logger.info("메서드 종료: $methodName")  // 예시 로그: [INFO] 2025-02-26 12:34:57 - 메서드 종료: getUserById
            
            return result
        } catch (e: Throwable) {
            // 로그 출력 예시: "메서드 오류: getUserById, 예외: NullPointerException"
            logger.error("메서드 오류: $methodName, 예외: ${e.message}")  // 예시 로그: [ERROR] 2025-02-26 12:34:57 - 메서드 오류: getUserById, 예외: NullPointerException
            throw e
        }
    }
}

 

  • @Aspect: 이 클래스가 Aspect임을 선언한다.
  • @Component: Spring의 빈으로 등록하여 자동으로 관리한다.
  • @Around: 지정한 Pointcut(여기서는 com.example.service 패키지의 모든 메서드)에 대해, 메서드 실행 전후로 Advice를 실행한다.
  • joinPoint.proceed(): 실제 메서드 호출을 진행하는 부분으로, 이 전후로 로그를 남겨 전체 실행 흐름을 모니터링한다.

이렇게 작성한 Aspect는 지정된 패키지 내의 모든 메서드 실행 시 자동으로 로그를 남긴다. 

 

서비스 클래스 예시

package com.example.service

import org.springframework.stereotype.Service

@Service
class UserService {
    fun getUserById(id: Long): User {
        // 사용자 조회 로직...
        return User(id, "Alice")
    }
}

 

이때 getUserById 메서드가 호출되면, Aspect의 joinPoint.signature.name은 "getUserById"를 반환하게 되고, 로그에도 "메서드 시작: getUserById"와 같이 출력된다.

 

하지만 이런 AOP를 사용시 주의할 점이 있다.

 

  • 과도한 사용 주의 : 너무 많이 사용하면 코드의 흐름을 파악하기 어려울 수 있다.
  • 디버깅 어려움 : AOP에 의해 동적으로 적용된 로직은 디버깅 시 실제 코드 흐름을 파악하기 어렵게 만들 수 있다.
  • 성능 고려 : Aspect가 많은 경우, 메서드 호출 전후에 추가적인 처리 시간이 발생할 수 있으니 성능에 미치는 영향을 고려해야한다.

 

 

Spring Boot는 빠르게 프로토타입을 만글고, 반복적인 설정 작업 없이 비즈니스 로직에 집중할 수 있게

Auto Configuration (자동 구성) 기능을 제공한다.

 

한번 Spring Boot Auto Configuration 기본 개념과 동작 원리, 활용예제에 대해서 알아보자.

 

Auto Configuration이란 무엇인가?

Auto Configuration은 개발자가 매번 복잡한 설정 파일이나 보일러플레이트 코드를 작성하지 않아도, 애플리케이션이 실행될 때 필요한 설정을 자동으로 구성해주는 기능이다. Spring Boot는 클래스패스에 포함된 라이브러리와 프로젝트 설정을 기반으로, 애플리케이션에 필요한(Bean)들을 자동으로 등록한다.

더보기

비유를 들어보자면 새로운 스마트폰을 구입했다고 생각해보자, 별도의 복잡한 설정 없이 기본 앱들과 기능들이 이미 준비되어 있는 상태일것이다. Spring Boot 의 Auto Configuration은 바로 그런 스마트폰과 같이, 개발자가 직접 설정하지 않아도 "기본값"으로 모든 것이 준비되도록 도와준다.

 

왜 Auto Configuration이 필요할까?

전통적인 Spring 애플리케이션은 XML이나 자바 기반의 설정 파일을 통해 수많은 설정을 해야 한다.( 정말 정말 복잡하고 귀찮은 작업이다..) 이러한 작업은 시간도 오래걸리고, 실수로 인한 오류가 발생하기 쉽다.

그렇기 때문에  Spring Boot 의 Auto Configuration 은 장점을 갖는다.

 

  • 개발 속도 향상: 복잡한 설정을 자동으로 처리하여 개발자가 비즈니스 로직에 집중할 수 있다.
  • 일관성 있는 설정: 기본적인 설정이 표준화되어 있어, 팀 내에서 일관된 개발 환경을 유지할 수 있다.
  • 쉬운 시작: 처음 프로젝트를 시작할 때 최소한의 설정만으로도 애플리케이션을 실행할 수 있다.

 

Auto Configuration의 동작 원리

Spring Boot의 Auto Configuration은 내부적으로 여러 가지 메커니즘과 어노테이션을 사용해 동작한다.

 

@EnableAutoConfiguration / @SpringBootApplication

Spring Boot 애플리케이션의 진입점에는 보통 @SpringBootApplication 어노테이션이 붙는다. 이 어노테이션은 

@EnableAutoConfiguration, @ComponentScan, @Configuration 등 여러 어노테이션을 합친 축약 표현이다.

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

 

@EnableAutoConfiguration : 해당 어노테이션은 Spring Boot에게 애플리케이션 시작 시 자동 구성을 활성화하라고 지시한다.

 

 

Auto Configuration은 클래스패스에 있는 라이브러리와 설정 파일을 확인한 후, 필요한 빈들을 등록한다.

이를 위해 조건부(Conditional) 어노테이션들이 사용된다. 대표적으로 @ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty 등이 있다.

 

예를 들어, H2 데이터베이스 라이브러리가 클래스패스에 있다면, Spring Boot는 자동으로 H2 데이터베이스 관련 빈을 등록한다.

만약 사용자가 직접 데이터 소스(DataSource)를 정의했다면, @ConditionalOnMissingBean 어노테이션 덕분에 자동 구성은 이를 무시하고 사용자가 정의한 설정을 우선시한다.

@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        // 기본 H2 데이터베이스 설정을 반환
        return new EmbeddedDatabaseBuilder()
                    .setType(EmbeddedDatabaseType.H2)
                    .build();
    }
}

 

 

 

Auto Configuration 파일의 구조

Spring Boot의 Auto Configuration 설정은 보통 spring.factories 파일에 정의되어 있다. 이 파일은 각 라이브러리별로 어떤 Auto Configuration 클래스를 적용할지 명시해 두며, 애플리케이션이 시작될 때 해당 클래스들이 로드된다.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfig.DataSourceAutoConfiguration,\
com.example.autoconfig.WebMvcAutoConfiguration

 

 

이렇게 장점만 있을것 같은 Auto Configuration도 한계가 있다.

 

  • 숨겨진 동작:
    자동으로 이루어지는 설정들이 때로는 개발자가 의도하지 않은 방식으로 동작할 수 있으므로, 내부 동작을 잘 이해해야 한다.
  • 디버깅의 어려움:
    Auto Configuration이 복잡하게 작동하는 경우, 문제가 발생하면 원인을 파악하기 어려울 수 있다.
  • 학습 곡선:
    초보자가 Auto Configuration의 내부 메커니즘을 완벽하게 이해하기까지는 시간이 걸릴 수 있다.

이러한 한계에도 불구하고, Auto Configuration은 대부분의 애플리케이션 개발에 있어 생산성과 유지보수성을 크게 향상시키는 도구임에는 분명하다.

자바로 개발을 하다 보면 가장 자주 발생하는 오류 중 하나가 NullPointerException 이다. (진짜...)

 

NullPointerException은 프로그램이 '값이 없다'는 상태인 null 값을 사용하려고 할 때 발생하는 오류이다. 정말 초보 개발자일때 뿐만 아니라 숙련이 되더라도 언제든 큰 골칫거리가 될 수 있다고 생각한다.

 

Kotlin은 이러한 문제를 근본적으로 해결하기 위해 Null Safety라는 개념을 도입했다. 

 

우선 Null에 대해서 정의를 해보자

 

 

Null이란 무엇인가?

Null은 "값이 없음"을 의미하는 특별한 값이다.

프로그래밍에서 어떤 변수에 값이 할당되지 않았거나, 사용할 수 없는 상태를 표현할 때 null을 사용한다. 예를 들어, 친구 목록을 저장하는 변수에 친구가 한 명도 없다면, 그 변수는 null일 수 있다.

var friend: String? = null // 친구가 없는 상태

 

여기서 중요한 점은 null을 허용하는 변수와 허용하지 않는 변수가 있다는 점이다.

 

만약 null인 값을 잘못 사용하면 프로그램은 오류를 내며 멈춰버릴 수 있다. 실제로, 많은 프로그래밍 오류는 이런 null 관련 문제에서 발생한다.

 

 

Kotlin의 Null Safety 기능

Kotlin은 변수 선언 시, 해당 변수가 null을 가질 수 있는지 여부를 명확하게 표시한다. 이를 통해 컴파일러가 자동으로 null 관련 오류를 사전에 경고해준다.

 

위에서 언급했던 Non-Nullable 변수와 Nullable 변수에 대해서 알아보자

  • Non-Nullable 변수 : 변수 선언 시에 기본적으로 null을 허용하지 않는다. 예를 들어, var name: String = "Alice" 라고 선언하면, 이 변수는 null 값을 가질 수 없다.
  • Nullable 변수 : 변수 뒤에 ?를 붙여 선언하면 null 값을 허용한다. 예를 들어, var friend: String? = null 이라고 선언하면, 이 변수는 null일 수도 있고, 실제 문자열 값을 가질 수도 있다.
fun main() {
    // Non-Nullable 변수: null 할당 불가
    var name: String = "Alice"
    // name = null  // 컴파일 에러 발생

    // Nullable 변수: null 할당 가능
    var friend: String? = null
    println("친구: $friend")
}

 

안전 호출 연산자 (?.)

Nullable 변수에 접근할 때, 직접 접근하면 null이 반환될 수 있어 오류가 발생할 수 있다. Kotlin에서는 안전 호출 연산아 "?." 를 사용하여 null일 때 안전하게 처리할 수 있다.

fun main() {
    var friend: String? = null
    // friend가 null이면 toUpperCase()를 호출하지 않고 전체 결과가 null로 처리됨
    println(friend?.toUpperCase())  // 출력: null

    friend = "Bob"
    println(friend?.toUpperCase())  // 출력: BOB
}

 

여기서 friend?.toUpperCase()는 friend가 null이 아니기 때문에 대문자로 변환하고, null이면 null을 그대로 반환한다.

 

 

엘비스 연산자 (?:)

안전 호출 연산자와 함께 엘비스 연산자 "?:" 를 사용하면, null인 경우에 기본 값을 제공할 수 있다.

fun main() {
    var friend: String? = null
    // friend가 null이면 "알 수 없음"을 대신 출력
    val friendName = friend ?: "알 수 없음"
    println("친구 이름: $friendName")
}

 

이 예제에서 friend가 null이기 때문에, "알 수 없음"이 friendName에 할당되어 출력된다.

 

 

let 함수

Kotlin에서는 null이 아닐 때만 특정 작업을 실행하는 간단한 방법으로 let 함수를 제공한다.

fun main() {
    var friend: String? = "Charlie"
    
    // friend가 null이 아닐 때만 블록 안의 코드 실행
    friend?.let {
        println("친구 이름 길이: ${it.length}")
    }
    
    friend = null
    // friend가 null이면 let 블록은 실행되지 않음
    friend?.let {
        println("이 코드는 실행되지 않습니다.")
    }
}

 

위처럼 let 함수는 nullable 변수가 null이 아닐 때 안전하게 해당 값을 사용할 수 있도록 도와준다.

1. 변수와 상수

Kotlin에서는 변수를 선언할 때 var 와 상수를 선언할 때 val 키워드를 사용한다.

  • var : 값이 변경 가능한 변수 
  • val : 값이 변경 불가능한 변수
fun main() {
    var age: Int = 20       // 가변 변수
    val name: String = "Alice"  // 불변 변수 (상수)

    println("이름: $name, 나이: $age")

    age = 21  // 값 변경 가능
    println("변경된 나이: $age")
}

 

 

2. 타입 추론

Kotlin은 변수의 타입을 자동으로 추론한다. 명시적으로 타입을 지정하지 않아도, 초기 값으로부터 타입을 유추할 수 있다.

fun main() {
    val language = "Kotlin"  // String 타입으로 추론됨
    val version = 1.5        // Double 타입으로 추론됨

    println("언어: $language, 버전: $version") // "언어 : Kotlin, 버전 : 1.5"
}

 

따라서 타입을 생략해도 컴파일러가 올바른 타입을 추론해주므로 코드가 간결해진다.

 

 

 

3. 문자열 템플릿

Kotlin에서는 문자열 내에서 변수 값을 쉽게 사용할 수 있는 문자열 템플릿 기능을 제공한다.

fun main() {
    val name = "Bob"
    val greeting = "안녕하세요, $name 님!"
    println(greeting)
}

 

문자열 내에 $name 과 같이 사용하면 해당 변수의 값이 문자열에 삽입된다.

 

 

 

4. 조건문

if 문

Kotlin의 if 문은 값으로 반환될 수 있어, 삼항 연산자 대신에 많이 사용된다.

fun main() {
    val score = 85
    val result = if (score >= 90) "합격" else "불합격"
    println("시험 결과: $result")
    
    
    // 식으로도 사용 가능
    val max = if( a > b) {
    	print("Choose a") // "Choose a" 를 출력하고 max에 a를 대입
        a
    } else {
    	print("Choose b") // "Choose b" 를 출력하고 max에 b를 대입
        b
    }
}

 

when 문

when 문은 여러 조건을 처리할 때 사용하며, Java의 switch 문보다 더 유연하다.

fun main() {
    val day = 3
    val dayName = when(day) {
        1 -> "월요일"
        2 -> "화요일"
        3 -> "수요일"
        4 -> "목요일"
        5 -> "금요일"
        6 -> "토요일"
        7 -> "일요일"
        else -> "알 수 없음"
    }
    println("오늘은 $dayName 입니다.")
    
    
    
    // 값 비교
    val x = 5

	when (x) {
    	1 -> println("x는 1이다")
    	2, 3 -> println("x는 2 또는 3이다")
    	in 4..10 -> println("x는 4와 10 사이에 있다")
    	else -> println("x는 다른 수이다")
	}
    
    
    // 타입 비교
    val data: Any = "Hello, World!"

    when (data) {
        is String -> println("data is a String: $data")
        is Int -> println("data is an Int: $data")
        is Boolean -> println("data is a Boolean: $data")
        else -> println("data is something else: $data")
    }


	// 조건식
    val y = 7

    val z = when {
        y == 5 -> "y는 5이다"
        y == 7 -> "y는 7이다"
    	else -> "y는 다른 수이다."
    }
    println(z)



}

 

 

 

5. 반복문

for 문

for 문은 컬렉션이나 범위(range)를 쉽게 순회할 수 있다.

fun main() {
    for (i in 1..5) {  // 1부터 5까지의 범위
        println("현재 값: $i")
    }
    
    val fruits = listOf("사과", "바나나", "체리")
    for (fruit in fruits) {
        println("과일: $fruit")
    }
}

 

 

while 문

기본적인 while 루프도 Java와 동일하게 사용할 수 있다.

fun main() {
    var count = 0
    while (count < 3) {
        println("카운트: $count")
        count++
    }
}

 

 

 

6. 함수

Kotlin에서 함수는 fun 키워드를 사용해 정의한다.

// 인사를 출력하는 함수
fun sayHello(name: String): String {
    return "안녕하세요, $name 님!"
}

fun main() {
    val message = sayHello("Charlie")
    println(message)
}

 

또, 함수의 몸체가 단 한줄이라면 중괄호와 return 키워드를 생략할 수도 있다.

fun add(a: Int, b: Int) = a + b

fun main() {
    println("5 + 3 = ${add(5, 3)}")
}

 

 

 

7. 클래스와 객체

Kotlin에서는 클래스를 사용해 객체 지향 프로그래밍을 할 수 있다. 

// Person 클래스 정의
class Person(val name: String, var age: Int) {
    fun introduce() = "제 이름은 $name이고, 나이는 $age살입니다."
}

fun main() {
    val person = Person("David", 30)
    println(person.introduce())
    
    // age 값 변경 가능 (var로 선언)
    person.age = 31
    println("나이 업데이트: ${person.age}")
}

 

Person 클래스를 정의하고, 생성자를 통해 name과 age 값을 초기화하며, 멤버 함수 introduce()로 자기소개를 한다.

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

Spring Boot Auto Configuration 이해해보기  (2) 2025.03.10
Kotlin의 Null Safety  (2) 2025.03.08
Spring Boot와 Kotlin: 기본 개념과 시작하기  (3) 2025.03.01
REST 와 RESTful  (0) 2024.02.07
Swagger  (1) 2023.11.08

복잡한 시스템을 신속하고 안정적으로 구축하는 것이 중요하다고 생각한다. 이에 따라 개발 생산성과 유지보수성을 극대화할 수 있는 도구와 언어의 선택은 필수적이다. Spring Boot 와 Kotlin은 이런 요구에 부합하는 강력한 조합으로 주목받고 있다.

 

Spring Boot란 무엇인가?

Spring Boot는 전통적인 스프링 프레임워크의 복잡한 설정 문제를 해결하기 위해 나온 경량화된 프레임워크입니다.
주요 특징과 장점을 구체적으로 살펴보면:

1. 자동 구성 (Auto-Configuration)

  • 설정의 단순화:
    Spring Boot는 애플리케이션 시작 시 프로젝트의 클래스패스와 설정 파일을 기반으로 자동으로 적절한 빈(Bean)들을 구성합니다.
    이는 개발자가 일일이 XML이나 복잡한 자바 설정을 작성할 필요 없이 비즈니스 로직에 집중할 수 있도록 도와줍니다.
  • 스프링 부트 스타터 (Starters):
    다양한 기능별 스타터 패키지를 제공하여, 의존성 관리가 간단하고, 필요한 라이브러리를 일괄 추가할 수 있습니다. 예를 들어, 웹 애플리케이션을 위한 spring-boot-starter-web이나 데이터베이스 연동을 위한 spring-boot-starter-data-jpa 등이 있습니다.

2. 독립 실행형 애플리케이션

  • 내장 서버:
    Tomcat, Jetty 등 내장 웹 서버가 포함되어 있어 별도의 서버 설치나 설정 없이 바로 실행할 수 있습니다.
    이로 인해 배포가 용이하고, 초기 개발 및 테스트 환경 구축이 신속해집니다.
  • 실행 파일:
    Spring Boot 애플리케이션은 단일 JAR 파일로 패키징되어, 명령어 한 줄로 실행할 수 있어 운영 환경에 배포하기 편리합니다.

3. 생산성과 유지보수성

  • 간결한 설정:
    복잡한 XML 설정이나 방대한 보일러플레이트 코드를 제거하여, 코드의 가독성과 유지보수성을 높입니다.
  • 모듈화 및 확장성:
    필요한 기능만 선택적으로 사용할 수 있도록 설계되어, 프로젝트가 커져도 쉽게 확장할 수 있습니다.

 

Kotlin의 기본 개념

Kotlin은 JetBrains에서 개발한 현대적인 프로그래밍 언어로, Java와 100% 호환되면서도 코드의 간결성과 안정성을 극대화합니다.
주요 개념과 특징은 다음과 같습니다.

1. 간결한 문법

  • 타입 추론:
    Kotlin은 변수 선언 시 타입을 명시하지 않아도 자동으로 타입을 추론하여 코드의 길이를 줄여줍니다.
    예를 들어, val name = "Kotlin"처럼 간단하게 선언할 수 있습니다.
  • 함수형 프로그래밍 지원:
    람다식, 고차 함수, 컬렉션 처리 등의 기능을 기본 제공하여, 반복적이거나 복잡한 로직을 짧고 명확하게 표현할 수 있습니다.
  • 데이터 클래스:
    단순히 데이터를 저장하기 위한 클래스를 작성할 때, data class 키워드를 사용하면 equals(), hashCode(), toString() 등 유용한 메서드를 자동 생성해 줍니다.

2. Null 안전성 (Null Safety)

  • 컴파일 단계에서의 검사:
    Kotlin은 Null에 안전한 타입 시스템을 도입하여, NullPointerException을 사전에 방지할 수 있습니다.
    변수에 ?를 붙여 Nullable 타입을 지정하는 방식으로, 코드 작성 시 Null 처리를 명확하게 할 수 있습니다.
  • 엘비스 연산자와 안전 호출:
    ?.와 ?: 등의 연산자를 사용하여 Null 체크를 간편하게 할 수 있어, 코드의 안정성을 높입니다.

3. 확장 함수와 프로퍼티

  • 기존 클래스에 기능 추가:
    확장 함수를 사용하면, 기존 클래스의 소스 코드를 수정하지 않고도 새로운 메서드를 추가할 수 있습니다.
    이를 통해 코드의 재사용성과 가독성을 크게 향상시킬 수 있습니다.
  • 프로퍼티 확장:
    확장 프로퍼티를 통해 기존 클래스에 새로운 속성을 추가하는 방식으로, 코드의 유연성을 높입니다.

4. 상호 운용성

  • Java와의 완벽한 호환:
    Kotlin은 JVM 언어로, Java 라이브러리와 자연스럽게 통합됩니다.
    기존의 Java 기반 프로젝트에 Kotlin을 점진적으로 도입할 수 있으며, 두 언어의 장점을 모두 활용할 수 있습니다.

 

왜 Spring Boot와 Kotlin을 함께 배워야 할까?

두 기술을 결합하여 학습하면 다음과 같은 여러 가지 장점이 있습니다.

1. 개발 생산성 향상

  • 간결함과 효율성 극대화:
    Kotlin의 간결한 문법과 null 안전성 덕분에 코드 작성 시간이 단축되고, 오류 가능성이 줄어듭니다.
    Spring Boot의 자동 구성과 스타터 패키지 기능은 복잡한 환경 설정을 단순화하여 개발 초기 진입 장벽을 낮춥니다.
  • 빠른 프로토타입 제작:
    두 기술 모두 빠른 애플리케이션 개발에 최적화되어 있어, 아이디어를 신속하게 구현하고 테스트할 수 있습니다.

2. 최신 개발 트렌드 반영

  • 모던한 언어와 프레임워크:
    Kotlin은 함수형 프로그래밍 패러다임을 포함한 최신 언어 기능들을 제공하며, Spring Boot는 클라우드 및 마이크로서비스 아키텍처에 적합한 설계 방식을 채택하고 있습니다.
  • 지속적인 업데이트와 커뮤니티 지원:
    두 기술 모두 활발한 커뮤니티와 정기적인 업데이트를 통해 최신 개발 트렌드를 반영하고 있어, 미래에도 경쟁력을 유지할 수 있습니다.

3. 안정성과 확장성

  • 코드의 안정성 강화:
    Kotlin의 컴파일 타임 Null 검사 및 안전한 타입 시스템은 런타임 오류를 줄여줍니다.
    Spring Boot의 모듈화된 아키텍처는 애플리케이션이 성장해도 체계적인 관리와 확장이 용이합니다.
  • 비즈니스 로직의 효과적 관리:
    Kotlin의 함수형 프로그래밍 기법과 확장 함수를 활용하면 복잡한 비즈니스 로직을 명확하고 직관적으로 관리할 수 있습니다.

4. 커뮤니티와 생태계의 장점

  • 풍부한 학습 자료와 오픈소스 프로젝트:
    Spring Boot와 Kotlin 모두 전 세계적으로 활발한 커뮤니티가 존재하며, 수많은 튜토리얼, 샘플 코드, 오픈소스 프로젝트가 학습에 큰 도움을 줍니다.
  • 실무 적용 사례:
    많은 기업과 스타트업들이 이미 이 조합을 채택해, 신속한 개발과 유지보수, 높은 확장성을 경험하고 있습니다. 실제 사례를 참고하면, 기술 선택의 타당성을 쉽게 이해할 수 있습니다.

 

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

Kotlin의 Null Safety  (2) 2025.03.08
Kotlin 기본 문법  (1) 2025.03.05
REST 와 RESTful  (0) 2024.02.07
Swagger  (1) 2023.11.08
Post API  (0) 2023.11.02

REST(Representational State Transfer) 란 무엇일까?

REST는 분산 시스템을 위한 아키텍쳐 스타일 중 하나이다. 리소스를 표현하고 해당 리소스에 대한 상태를 전송하는 데 사용된다. 웹 서비스의 디자인에 널리 사용되며, 클라이언트와 서버 간의 통신을 위한 규칙과 제약을 정의한다.

 

REST의 주요 특징

 

1. Client-Server Architecture (클라이언트-서버 아키텍쳐) 

  클라이언트와 서버가 서로 독립적으로 개발되고 확장될 수 있도록 설계된다. 이를 통해 서버와 클라이언트의 역할을 분리하여 시스템의 유연성을 향상시킨다.

 

2. Statelessness (무상태성)

  각 요청은 클라이언트의 모든 정보를 포함하며, 서버에서 클라이언트의 상태를 유지하지 않는다. 이는 서버측의 부하를 줄이고 확장성을 향상시킨다.

 

3. Cacheability (캐시 가능성)

  HTTP 프로토콜을 사용하여 데이터를 캐싱할 수 있도록 한다. 이는 서버의 부하를 줄이고 네트워크 성능을 향상시킨다.

 

4. Uniform Interface (일관된  인터페이스)

  리소스에 대한 표현과 상호작용 방법이 일관성 있게 정의되어야 한다. 이를 통해 시스템의 분리성을 유지하고 상호 운용성을 향상시킨다.

 

5. Layered System (계층화 시스템)

  클라이언트는 서버와 직접 통신하는 대신, 중간에 프록시나 캐시 서버 등을 통해 통신할 수 있다. 이는 시스템의 확장성과 보안을 향상시킨다.

 

 

REST의 요소

 

1. 리소스(Resource)

  데이터를 나타내는 개체이다,. 모든  리소스는 고유한 URI(Uniform Resource Identifier)로 식별된다.

 

2. 메서드(Method) 

  HTTP 메서드 (GET,POST,PUT,DELETE 등)를 사용하여 리소스에 대한 행동을 정의한다.

 

3. 메시지(Message) 

  클라이언트와 서버 간의 통신에 사용되는 데이터이다. 일반적으로 JSON 또는 XML 형식으로 표현된다.

 

4. 표현(Representation) 

  클라이언트가 리소스에 접근할 때 얻는 데이터의 형식을 나타낸다. 일반적으로 JSON 또는 XML 형식으로 표현된다.

 

요약하자면 REST는 웹 서비스 및 분산 시스템의 디자인에 널리 사용되며, 간단하고 유연한 아키텍쳐 스타일을 제공하여 시스템의 확장성과 상호 운용성을 향상시킨다.

 

 

 

 

RESTful

RESTful은 REST 아키텍쳐 스타일을 따르는 웹 서비스를 설계하고 구현하는 방법을 의미한다. 즉 RESTful 한 웹 서비스는 REST의 원리와 제약을 준수하여 개발된 웹 서비스를 말한다. 클라이언트와 서버 간의 상호 작용을 위해 HTTP 프로토콜을 기반으로 하며, 자원을 표현하고 그 상태를 전송하기 위해 표준 HTTP 메서드 (GET,POST,PUT,DELETE 등)를 사용한다.

 

 

RESTful 웹 서비스의 장점

 

1. 간결한 인터페이스 

  HTTP 프로토콜을 사용하여 간단하고 직관적인 API 를 제공한다.

 

2. 유연성 

  다양한 플랫폼 및 기기에서 사용할 수 있으며, 다양한 데이터 형식을 지원한다 ex) 웹, 모바일 등 여러 시스템에서 사용 가능 

 

3. 확장성 

  클라이언트와 서버 간의 분리로 확정성이 향상된다.

 

4. 상호 운용성

  다른 시스템과의 통합이 쉽고 간단하다

 

5. 자유로운 사용

  웹 기술과 상호 운용이 용이하므로 다양한 언어와 프레임 워크로 개발할 수 있다.

 

 

그렇다면 예시를 통해  REST API 에 대해서  알아보자

 

REST API

REST API 설계 시 가장 중요한 항목은 다음의 2가지로 요약할 수 있다.

 

1. URI는 정보의 자원표현해야 한다.

2. 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE) 로 표현한다.

 

 

- URI는 정보의 자원을 표현해야 한다. *리소스명은 동사보다는 명사를 사용해야 한다.

GET /members/delete/1

 

위와 같은 방식은 REST를 제대로 적용하지 않은 URI이다. URI는 자원을 표현하는데 중점을 두어야 한다. 행위에 대한 표현이 들어가서는 안된다.

 

자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE) 로 표현한다,.

위의 잘못 된 URI를 HTTP Method를 통해 수정해 보면

DELETE /members/1

 

 

이와 같이 수정할 수 있다. 

 

어떠한 정보를 가져올때는 GET, 회원 추가, 수정 등 행위를 표현하고자 할 때는 POST Method를 사용하여 표현할 수 있다.

 

회원정보를 가져오는 URI

GET /members/show/1 (x)
GET /members/1      (o)

 

회원을 추가할 때

GET /members/insert/2  (x) - GET 메서드는 리소스 생성에 맞지 않다.
POST /members/2 	   (o)

 

제품관리 

GET /products 		-- 모든 제품 목록을 가져온다
GET /products/{id}	-- 특정 제품의 정보를 가져온다
POST /products		-- 새로운 제품을 생성한다
PUT /products/{id}	-- 특정 제품의 정보를 업데이트한다
DELETE /products/{id} -- 특정 제품을 삭제한다

 

HTTP Method

 

URI 설계 시 주의할 점

 

1. 슬래시 구분자(/)는 계층 관계를 나타내는 데 사용한다.

2. URI 마지막 문자로 슬래시(/)를 포함하지 않는다.

3. 하이픈(-)은 URI 가독성을 높이는데 사용 (불가피하게 긴 URI 경로를 가지게 된 경우)

4. 밑줄(_)은 URI에 사용하지 않는다.

5. URI 경로에는 소문자가 적합하다.

6. 파일 확장자는 URI에 포함시키지 않는다. (Accept Header 를 사용한다)

 

 

HTTP 응답코드

HTTP 상태코드

 

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

Kotlin 기본 문법  (1) 2025.03.05
Spring Boot와 Kotlin: 기본 개념과 시작하기  (3) 2025.03.01
Swagger  (1) 2023.11.08
Post API  (0) 2023.11.02
Spring Boot 기초  (0) 2023.11.01

Swagger

- 협업을 위해 필요한 라이브러리

- 서버로 요청되는 API 리스트를 HTML 화면으로 문서화하여 테스트 할 수 있는 라이브러리

- 서버가 가동되면서 @RestController를 읽어 API를 분석하여 HTML 문서를 작성한다.

 

Swagger가  필요한 이유

- REST API의 스펙을 문서화 하는 것은 매우 중요하다. API를 변경할 때마다 Reference 문서를 계속 바꿔야하는 불편함이 있다.

 

Swagger 설정 방법

@Configuration : 어노테이션 기반의 환경 구성을 돕는 어노테이션 IoC Container에게 해당 클래스를 Bean 구성 Class 임을 알려준다.

 

@Bean : 개발자가 직접 제어가 불가능한 외부 라이브러리 등을 Bean으로 만들 경우에 사용

 

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

Spring Boot와 Kotlin: 기본 개념과 시작하기  (3) 2025.03.01
REST 와 RESTful  (0) 2024.02.07
Post API  (0) 2023.11.02
Spring Boot 기초  (0) 2023.11.01
REST API  (2) 2023.10.30

리소스를 추가하기 위해 사용되는 API

@PostMapping : POST API를 제작하기 위해 사용되는 어노테이션 @RequestMapping + POST method의 조합이다.

일반적으로 추가 하고자 하는 Resouce를 http body에 추가하여 서버에 요청한다. 그렇기 때문에 @RequestBody를 이용하여 body에 담겨있는 값을 받아야 한다.

 

// http://localhost:8080/api/test/vi/post-api/member
// 해당 URL로 member 데이터를 넘겨주면 해당 데이터의 키값과 value값을 return 하는 예제
@PostMapping(value="/member")
public String postMember(@RequestBody Map<String, Object> postData) {
	StringBuilder sb = new StringBuilder();
    
    postData.entrySet().forEach(map -> {
    	sb.append(map.getKey() + ":" + map.getValue() + "\n");
    });
    
    return sb.toString();
}





// DTO를 사용하는 방식
// key와 value가 정해져있지만, 받아야할 파라미터가 많을 경우 DTO객체를 사용한다.
// GetMapping 과 다른점은 @RequestBody를 꼭 붙여야 한다는 것이다.
@PostMapping(value-"/member2")
public String postMemberDto(@RequestBody MemberDto memberDto) {
	return memberDto.toString();
}

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

REST 와 RESTful  (0) 2024.02.07
Swagger  (1) 2023.11.08
Spring Boot 기초  (0) 2023.11.01
REST API  (2) 2023.10.30
Maven 과 Gradle  (0) 2023.10.29

컨트롤러 (Controller)

모델(Model)과 뷰(View) 사이에서 브릿지 역할을 수행한다. 앱의 사용자로부터 입력에 대한 응답으로 모델 및 뷰를 업데이트 하는 로직을 포함하여 사용자의 요청은 모두 컨트롤러를 통해 진행되어야 한다. 컨트롤러로 들어온 요청은 모델이 어떻게 처리할지 결정하여 모델로 요청을 전달한다.

 

@RestController

  • Spring Framework 4 버전부터 사용가능한 어노테이션
  • @Controller에 @ResponseBody가 결합된 어노테이션
  • 컨트롤러 클래스 하위 메소드에 @ResponseBody 어노테이션을 붙이지 않아도 문자열과 JSON 등을 전송할 수 있음
  • View를 거치지 않고 HTTP ResponseBody에 직접 Return 값을 담아 보내게 된다.

 

 

@RquestMapping

MVC의 핸들러 매핑(Handler Mappin)을 위해서 DefaultAnnotationHandlerMapping을 사용한다. DefaultAnnotationHandlerMapping 매핑정보로 @RequestMapping 어노테이션을 활용 클래스와 메소드의 RequestMapping을 통해 URL을 매핑하여 경로를 설정하여 해당 메소드에서 처리한다.

 

value : url 설정

method : GET, POST, DELETE, PUT, PATCH 등

 

스프링 4.3버전 부터 메소드를 지정하는 방식보다 간단하게 사용할 수 있는 어노테이션을 사용할 수 있음

  • @GetMapping
  • @PostMapping
  • @DeleteMapping
  • @PutMapping
  • @PatchMapping

 

 

 

 

'Back-End > Spring Boot + Kotlin' 카테고리의 다른 글

REST 와 RESTful  (0) 2024.02.07
Swagger  (1) 2023.11.08
Post API  (0) 2023.11.02
REST API  (2) 2023.10.30
Maven 과 Gradle  (0) 2023.10.29

+ Recent posts