앞서 로그와 로그를 관리하는게 얼마나 중요한지 알아보았는데 중앙 집중식 로그 관리에 대해 알아두면 좋을것 같아서 정리를 했다.

 

중앙 집중식 로그 관리의 필요성

1. 문제 진단과 모니터링

서버에 문제가 발생했을 때, 로그를 통해 문제의 원인을 빠르게 파악할 수 있다. 중앙 집중식 로그 관리 시스템은 모든 로그를 실시간으로 모니터링하고, 이상 징후가 보이면 즉시 알림을 보내어 빠른 대응이 가능하게 해준다.

 

2. 보안 및 규정 준수

로그는 보안 사건을 추적하는 데도 유용하다. 로그를 중앙에서 관리하면, 보안 침해나 이상 행동을 쉽게 감지할 수 있고, 법정 규정을 준수하기 위한 증거 자료로 활용할 수도 있다.

 

3. 성능 분석과 최적화

로그 데이터를 분석하면 애플리케이션의 성능 병목이나 자원 사용 패턴을 파악할 수 있다. 이 정보를 바탕으로 시스템의 성능을 최적화하는 전략을 수립할 수 있다.

 

 

중앙 집중식 로그 관리 시스템의 구성 요소

1. 로그 수집기 (Filebeat, Logstash 등)

  • Filebeat : 각 서버에서 로그 파일을 읽어 들여 중앙 서버(Elasticsearch 등)로 전송하는 가벼운 에이전트이다.
  • Logstash : 수집된 로그 데이터를 필터링, 변환, 그리고 적절한 포맷으로 재가공하여 저장소에 전달하는 역할을 한다.

 

2. 로그 저장소 (Elasticsearch 등) 

  • Elasticsearch : 로그 데이터를 저장하고, 빠른 검색과 분석이 가능하도록 하는 분산형 데이터베이스이다. 수백만 건의 로그도 실시간으로 검색할 수 있다.

 

3. 로그 조회 및 시각화 도구 (Kibana 등)

  • Kibana : Elasticsearch에 저장된 로그 데이터를 시각화하고, 대시보드 형태로 모니터링 할 수 있는 웹 인터페이스 도구이다. 사용자는 Kibana를 통해 로그 검색, 필터링, 시각화 작업을 쉽게 수행할 수 있다.

 

 

중앙 집중식 로그 관리 아키텍쳐

중앙 집중식 로그 관리 시스템은 다음과 같은 흐름으로 구성됩니다:

  1. 로그 생성:
    각 애플리케이션 서버에서 로그가 생성됩니다.
  2. 로그 수집:
    Filebeat나 Logstash 같은 에이전트가 로그 파일을 읽어 옵니다.
  3. 로그 전송:
    수집된 로그 데이터는 네트워크를 통해 중앙 로그 저장소(Elasticsearch)로 전송됩니다.
  4. 로그 저장:
    Elasticsearch는 로그 데이터를 색인(index)하여 저장하고, 빠른 검색이 가능하도록 합니다.
  5. 로그 조회 및 분석:
    Kibana를 통해 사용자가 로그 데이터를 검색하고 시각화하여 분석할 수 있습니다.

 

데이터 흐름과 처리 과정

  • 생성 단계:
    각 서버에서는 Logback이나 Log4J와 같은 로깅 프레임워크가 로그 메시지를 파일에 기록합니다.
  • 수집 단계:
    Filebeat가 정해진 경로의 로그 파일을 지속적으로 모니터링하고, 새로운 로그가 생성되면 이를 읽어들입니다.
  • 전송 단계:
    Filebeat는 읽어들인 로그 데이터를 JSON 등 표준 형식으로 변환해 Elasticsearch 클러스터로 보냅니다.
  • 저장 단계:
    Elasticsearch는 전송된 로그 데이터를 색인하고, 사용자가 빠르게 검색할 수 있도록 합니다.
  • 조회 단계:
    Kibana 대시보드에서 사용자는 검색 쿼리를 입력하거나 필터를 적용해 원하는 로그 메시지를 찾을 수 있습니다.

 

 

로그 전송을 위한 Filebeat 설정 예

 

Filebeat를 우선 설치하고, 각 서버에 다음과 같은 설정 파일 filebeat.yml  을 사용하여 로그를 중앙 서버로 전송한다.

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /path/to/your/app/logs/*.log

output.elasticsearch:
  hosts: ["http://elasticsearch_server:9200"]

 

위 설정은 Filebeat가 /path/to/your/app/log/ 폴더에 있는 모든 로그 파일을 모니터링하고, 새로운 로그가 생기면 Elasticsearch로 전송하도록 한다.

 

 

 

중앙 집중식 로그 관리 시스템 구축 시 고려사항

1. 보안 및 개인정보 보호

  • 로그에는 민감한 정보가 포함될 수 있으므로, 로그 데이터에 접근할 수 있는 권한을 제한해야 합니다.
  • 로그 전송 시 암호화와 인증을 적용해, 로그 데이터가 중간에 탈취되지 않도록 해야 합니다.

2. 성능 및 확장성 고려

  • 로그 수집 에이전트(Filebeat 등)가 과도한 자원을 사용하지 않도록 최적화해야 합니다.
  • Elasticsearch 클러스터는 로그 데이터의 양과 검색 빈도에 맞춰 확장할 수 있어야 합니다.

3. 장애 대응 및 복원 전략

  • 중앙 집중식 로그 관리 시스템 자체가 장애가 발생할 경우, 로그를 잃을 수 있으므로 이중화(High Availability)를 고려해야 합니다.
  • 백업 및 복원 정책을 수립해, 중요한 로그 데이터를 안전하게 보관해야 합니다.

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

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

 

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

 

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 스택의 장점

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

 

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

처음 회사에 갔을때 this에 대해 잘 몰라서 로직을 파악하기 너무 힘들었던 기억이 난다.. 그 이후 this 에 대해서 찾아보기도 많이 찾아보고 잘 알고 있다고 생각했지만 설명하라고 해보면 하기가 막막한것 같아서 this에 관해 확실히 정리하고 넘어가면 좋을것 같아서 이 글을 작성한다.

 

자바스크립트의 this 란?

생각해보면 우리 모두 자기 자신을 "나"라고 부른다. 자바스크립트도 비슷하다. 함수나 객체 안에서 this는 "내가 누구인가?"를 알려주는 역할을 한다.

 

즉 "내 자신" 또는 "내가 속한 주체"를 가리키는 특별한 키워드이다.

 

this를 왜 사용할까? 

 

코드를 작성할 때 객체의 내부 함수에서 매번 객체의 이름을 쓰는 대신 this를 사용하면 코드가 더 깔끔해지고, 객체의 이름이 바뀌어도 수정이 편해진다.

 

이해가 잘 안간다.. 그렇다면 직접 해보는게 가장 좋다.  this 없이 객체의 속성에 접근해보자

const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + person.name + " 이야!");
  }
};

person.greet();
// 실행 결과: 안녕, 나는 Bob 이야!

 

여기서 문제점이 발생한다. 만약 person의 이름이 바뀐다면? 직접 함수 내부의 person.name 의 값을 변경해줘야 한다.

 

반면 this를 사용한 경우는

const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + this.name + " 이야!");
  }
};

person.greet();
// 실행 결과: 안녕, 나는 Bob 이야!

 

객체의 이름을 변경해도 내부 코드를 수정할 필요가 없다. 왜냐하면 this는 항상 현재 객체를 가리키기 때문이다.

 

이해가 되는가? 설명이 좀 부족해보인다. 다른 예시를 들어보자,

this없이 객체 속성에 접근한 예시를 하나 더 살펴보자

const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + person.name + " 이야!");
  }
};

const anotherPerson = {
  name: "Alice",
  // person 객체의 greet 함수를 재사용
  greet: person.greet
};

person.greet();
// 실행 결과: 안녕, 나는 Bob 이야!

anotherPerson.greet();
// 실행 결과: 안녕, 나는 Bob 이야! -> 기대했던 "Alice"가 아니라 "Bob"이 출력됨

 

이제야 이해가 된다! 위 예시를 보니 this가 정말 편리하다고 느껴진다.

 

1. 객체 안에서의 this

1-1 메서드 호출과 this

객체 내부의 함수(메서드를 호출할 때, this는 자동으로 그 객체를 가리킨다. 즉, 객체이름.메서드() 형태로 호출하면, 메서드 내부의 this는 객체이름이 된다.

 

const student = {
  school: "초등학교",
  study: function() {
    console.log("나는 " + this.school + "에서 공부해!");
  }
};

student.study();
// 실행 결과: 나는 초등학교에서 공부해!

 

 

this.school은 student 객체 안의 school 속성을 가져온다.

 

 

1-2 메서드 내부에서의 this

메서드 내부에 this를 사용하면 어떨까?

const user = {
	name : "Alice",
    greet: function() {
    	console.log(this);
    }
};


user.greet();
// 출력 : {name : '홍길동', greet : [Function: greet] }

 

위의 코드에서 this 는 greet 메서드를 호출한 객체인 user를 가리킨다. 자바스크립트는 메서드가 호출될 때, 호출한 객체를 자동으로 this에 바인딩한다. 

 

메서드 호출 방식이 this의 동작을 결정짓는 키이다.

 

 

 

더보기
더보기

1-3 중첩 함수에서의 this 문제

 

객체의 메서드 안에 또 다른 함수를 만들면, 이 내부 함수에서는 this가 예상과 다르게 동작할 수 있다.

 

중첩 함수와 this 예제를 보자

const teacher = {
  subject: "수학",
  explain: function() {
    function inner() {
      console.log("나는 " + this.subject + "를 가르쳐!");
    }
    inner();
  }
};

teacher.explain();
// 실행 결과: 브라우저에서는 undefined 혹은 전역 객체의 subject (대부분 undefined)

 

해결방법은

  1. 외부 변수에 this 저장하기(self, that 사용)
  2. 화살표 함수 사용하기

가 있다.

 

 

self를 사용하기

const chef = {
  dish: "파스타",
  cook: function() {
    const self = this;  // this를 self라는 변수에 저장
    function innerCook() {
      console.log("오늘의 요리는 " + self.dish + "입니다!");
    }
    innerCook();
  }
};

chef.cook();
// 실행 결과: 오늘의 요리는 파스타입니다!

 

self 변수에 this를 저장하여 내부 함수에서도 외부의 this를 참조할 수 있게 한다.

 

2. 일반 함수에서의 this

 

2-1 함수 호출 방식에 따른 this 변화

함수를 그냥 따로 실행할 때도 있는데, 그 경우 this가 가리키는 대상이 달라질 수 있다. 

 

전역 함수에서의 this를 살펴보자

function showThis() {
  console.log(this);
}

showThis();
// 실행 결과 (브라우저의 경우): Window 객체(전역 객체)가 출력됨

 

이렇게 호출하면 this는 브라우저에서는 window 객체를, Node.js에서는 전역 객체를 가리킨다.

 

 

 

3. 화살표 함수와 this

화살표 함수란 무엇인가? 

화살표 함수 (Arrow Function)는 짧게 함수를 작성할 수 있게 도와주고, 특별한 점은 자신만의 this를 자지지 않는다는 것이다.

 

엥? 이건 또 무슨 말인가..

 

화살표 함수 내부의 this는 바깥 함수의 this를 그대로 사용한다.

 

예제를 봐보자

const person = {
  name: "Alice",
  sayName: function() {
    const innerFunc = () => {
      console.log("내 이름은 " + this.name);
    }
    innerFunc();
  }
};

person.sayName();
// 실행 결과: 내 이름은 Alice

 

innerFunc 는 화살표 함수이기 때문에 자신의 this를 생성하지 않고, 바로 sayName 함수의 this, 즉 person 객체를 그대로 사용한다.

 

 

화살표 함수의 장단점

 

장점 : 

  • 코드가 간결해지고,  중첩 함수에서도 외부 this를 그대로 사용할 수 있다.

단점 : 

  • 자체 this가 없기 때문에, 특정 상황에서는 의도한 대로 동작하지 않을 수 있다. 
const animal = {
  type: "고양이",
  speak: () => {
    console.log("나는 " + this.type);
  }
};

animal.speak();
// 실행 결과: 나는 undefined (혹은 전역 객체의 type 값)
// 왜냐하면 화살표 함수는 자신의 this가 없으므로 전역 this를 사용하기 때문임

 

위의 예시를 통해 객체의 메서드를 정의할 때는 일반 함수를 사용하는 것이 좋다는 점을 알 수 있다.

 

 

 

4. 콜백 함수에서의 this

일반 함수와 마찬가지로, 콜백 함수 내부의 this는 함수가 어떻게 호출되는지에 따라 달라진다. 

문제는, 콜백 함수로 전달된 함수는 호출될 때 원래의 객체 컨텍스트와는 별개로 호출되는 경우가 많다는 것이다.

 

 const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + this.name + " 이야!");  // 올바른 출력: "안녕, 나는 Bob 이야!"
    
    // setTimeout에 일반 함수 콜백 전달
    setTimeout(function() {
      // 이 내부의 this는 전역 객체(window) 또는 undefined(엄격 모드)로 설정됨
      console.log("안녕, 나는 " + this.name + " 이야! (setTimeout 내부)");
    }, 1000);
  }
};

person.greet();
// 실행 결과:
// 즉시 출력: 안녕, 나는 Bob 이야!
// 1초 후 출력 (브라우저 일반 모드): 안녕, 나는 undefined 이야!

 

위 코드가 어떻게 동작하는지 잠시 생각해보자.

 

왜 저런 결과가 나오는 것일까?

  • person.greet() 에서의 this는 person 객체를 가리킨다
  • 하지만, setTimeout에 전달된 일반 함수 콜백은 전역 함수처럼 호출되기 때문에, 그 내부의 this는 전역 객체(브라우저에서는 window)를 가리킨다.
  • 만약 전역 객체에 name이라는 속성이 없다면 undefined가 출력되는 것이다

 

4-1 이벤트 핸들러에서의 this

웹 페이지의 이벤트 핸들러에서도 콜백 함수의 this 동작은 중요하다. 이벤트 핸들러에서의 this는 주로 이벤트를 발생시킨 DOM 요소를 가리킨다.

 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>이벤트 핸들러에서의 this 예제</title>
  </head>
  <body>
    <button id="myButton">클릭해봐!</button>
    <script>
      const button = document.getElementById("myButton");
      
      // 일반 함수 콜백의 경우, this는 버튼 요소를 가리킴
      button.addEventListener("click", function() {
        console.log("버튼 텍스트 (일반 함수): " + this.innerText);
      });
      
      // 화살표 함수 콜백의 경우, this는 상위 스코프의 this를 상속받음 (이 경우 window)
      button.addEventListener("click", () => {
        console.log("버튼 텍스트 (화살표 함수): " + this.innerText);
      });
    </script>
  </body>
</html>

 

일반 함수 콜백에서는 버튼의 텍스트가 정상적으로 출력되는걸 볼 수 있다. 하지만 화살표 함수 콜백에서는 this가 전역 객체를 참조하기 때문에, this.innerText가 기대한 값이 나오지 않는다.

 

즉 일반 함수 콜백으로 사용시 이벤트 핸들러로 등록된 일반 함수(즉, function 키워드로 작성된 함수)는 호출될 때 this가 해당 이벤트를 발생시킨 요소(예: 버튼, 링크 등)를 가리킨다.

 

 <button id="myButton">클릭해봐!</button>
<script>
  const button = document.getElementById("myButton");
  button.addEventListener("click", function() {
    // 이 내부에서의 this는 클릭한 버튼 요소 자체를 가리킴
    console.log("버튼의 텍스트는: " + this.innerText);
  });
</script>

 

버튼을 클릭하면 콘솔에 "버튼의 텍스트는: 클릭해봐!" 가 출력된다.

 

 

'Front-End > JavaScript' 카테고리의 다른 글

원시 값과 객체의 비교  (1) 2024.07.24
함수(2)  (0) 2024.07.21
함수(1)  (1) 2024.06.18
함수 표현식  (1) 2024.06.17
스코프  (4) 2024.06.14

+ Recent posts