GROWTH LOG STORIES

함께 만든 성장을,
기록으로 연결합니다.

AI와 함께 배우고, 직접 만들고, 서로 나눈
그로스로그 멤버들의 생생한 성장 기록을 만나보세요.

성장 기록
5기+이어진 커뮤니티
HEALTHIO
PROJECT

사진 한 장으로 시작하는 건강 루틴, Health IO

대표 이야기 읽기

ALL STORIES

성장의 순간을
한곳에서 만나보세요.

프로젝트의 시작부터 모임에서 나눈 인사이트까지,
그로스로그가 함께 만든 기록을 모았습니다.
개의 성장 기록

성장일지

[GL 3기] 자바스크립트가 코드를 실행하는 환경, 그리고 돌아가는 서버를 숫자로 지켜보기 (성장일지 6회차 큐레이션 ①) 🌱

GROWTH LOG🌱 2026. 8. 7. 05:36

안녕하세요! 그로스로그 입니다!😊 🌱

this가 왜 매번 다른지를 밑바닥부터 따라간 기록, 그리고 돌아가는 서버를 숫자로 지켜보는 시스템을 만든 기록. 3기 6회차 첫 번째입니다.

성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 매 회차 운영진이 모든 글을 읽고, 그중 특히 마음에 남은 글을 함께 골라 큐레이션합니다.

이번 회차는 큐레이션이 네 편이라 두 번에 나눠 소개합니다. 먼저 관찰하는 방식에 대한 두 편이에요. 하나는 안에서, 하나는 밖에서 봅니다.

 

01

🧩 실행 컨텍스트란?

이종경 님

이종경 님 원문에 실린 이미지
이런 내용이에요
실행 컨텍스트(Execution Context) — 자바스크립트 코드가 실행되는 환경 정보의 모음입니다.
글은 이걸 세 요소로 나눠 설명해요. Variable Environment · Lexical Environment · This Binding. 그리고 각각이 무엇을 담고 왜 나뉘어 있는지를 짚습니다.
여기에 this 바인딩 6가지 규칙클로저가 왜 메모리에 남는지까지 이어집니다. 자바스크립트 시리즈의 11번째, 마지막 글이에요.

2회차의 런타임 글에서 "콜 스택에 실행 컨텍스트가 쌓인다"고 했었죠. 이번엔 그 실행 컨텍스트 안이 어떻게 생겼는지입니다.

조금 더 들어가 보면

이 글에서 가장 값진 부분은 왜 환경이 두 개인가를 설명한 대목입니다.

Variable EnvironmentLexical Environment는 처음엔 똑같은 내용으로 시작합니다. 그런데 왜 굳이 둘로 나눠뒀을까요? 글의 답이 명확합니다.

"블록 스코프의 let/const 변수가 생길 때 새 렉시컬 환경을 만들어도, var 선언은 여전히 기존 함수 레벨 환경에 추가하기 위함입니다."

varlet의 스코프가 다르기 때문이에요. var는 함수 단위, let은 블록 단위. 이 두 규칙을 동시에 지키려면 저장소가 두 개여야 합니다.

function scopeTest() {
  var x = 1;          // 함수 스코프 (VariableEnvironment에 등록)
  if (true) {
    var x = 2;        // 같은 함수 스코프의 x를 다시 선언 (기존 var x를 변경)
    let y = 3;        // 블록 스코프 (새 LexicalEnvironment에 등록)
    console.log(x);   // 2
    console.log(y);   // 3
  }
  console.log(x);     // 2 -> 블록에서 바꾼 값이 유지된다
  // console.log(y);  // ReferenceError -> 블록을 벗어나면 접근 불가
}

블록에 들어가는 순간 새 Lexical Environment가 만들어집니다. let y는 거기 들어가요. 그런데 var x는 그 새 환경을 무시하고 함수의 Variable Environment로 찾아 올라갑니다. 그래서 블록 밖에서도 값이 유지되죠.

"var는 블록을 무시한다"를 외우는 것과, 저장소가 다르다는 걸 아는 건 다릅니다. 후자를 알면 처음 보는 상황에서도 예측할 수 있어요.

🧭 this가 매번 다른 이유

글이 this 바인딩을 여섯 가지로 정리한 부분이 실용적입니다. 그런데 관통하는 원리가 하나 있어요.

this는 함수가 선언된 곳이 아니라 호출된 방식으로 정해집니다.

· 일반 함수 호출 — 전역 객체 (엄격 모드에서는 undefined)

· 메서드 호출 — 호출한 객체

· 생성자 호출(new) — 새로 만들어진 객체

· call/apply/bind — 지정한 객체

· 화살표 함수감싸고 있는 외부 컨텍스트의 this

마지막 하나만 규칙이 다릅니다. 그래서 글의 예제가 좋아요.

const arrowFunc = () => console.log(this);
obj.arrowMethod = arrowFunc;
obj.arrowMethod();   // window (화살표 함수는 obj에 바인딩되지 않음)

객체의 메서드로 붙였는데도 thisobj가 아닙니다. 화살표 함수는 정의될 때 이미 this가 정해지기 때문이에요.

이게 실무에서 갈립니다. setTimeout이나 이벤트 핸들러 안에서 this가 엉뚱한 걸 가리키는 문제는 화살표 함수로 풀리고, 반대로 객체 메서드를 화살표 함수로 쓰면 오히려 깨집니다. 규칙이 다르다는 걸 알아야 골라 쓸 수 있어요.

🔒 클로저가 메모리에 남는 이유

글의 마지막 부분이 앞의 설명과 정확히 이어집니다.

보통 함수 실행이 끝나면 그 실행 컨텍스트와 지역 변수는 스택에서 사라지고 GC 대상이 됩니다. 그런데 클로저는 다릅니다.

"내부 함수가 살아있는 한, 내부 함수가 참조하는 외부 Lexical Environment에 있는 변수들도 가비지 컬렉션이 되지 않고 유지되는 것입니다."
function createCounter() {
  let count = 0;
  return function () {
    count++;
    console.log(`현재 count 값: ${count}`);
  };
}

const counterA = createCounter();
counterA();   // 현재 count 값: 1
counterA();   // 현재 count 값: 2

const counterB = createCounter();   // 새로운 카운터 (별도 렉시컬 환경)
counterB();   // 현재 count 값: 1

createCounter()는 이미 끝났는데 count가 살아 있습니다. 반환된 함수가 그 환경을 참조하고 있기 때문이에요. 그리고 counterB완전히 다른 환경을 갖습니다.

그런데 글이 여기에 경고를 붙입니다.

"동시에 불필요하게 오래 참조가 남아있는 변수들은 메모리를 점유하는 부작용도 있습니다. 따라서 클로저를 사용할 때는 필요 없어진 참조를 제거하여 메모리 누수를 방지하는 것이 중요합니다."

클로저는 기능이자 메모리 누수의 원인입니다. 이벤트 핸들러를 등록하고 안 지우면, 그 핸들러가 붙들고 있는 환경 전체가 남거든요. 5회차의 removeEventListener 이야기와 정확히 이어지는 지점입니다.

큐레이터 노트

한 주제를 열한 편까지 밀고 간 시리즈의 마지막이라 뽑았습니다.

이 저자의 자바스크립트 시리즈는 2회차의 런타임에서 시작해 클래스를 거쳐 실행 컨텍스트로 끝납니다. 바깥에서 안으로 들어가는 순서예요. 엔진과 호스트 환경 → 콜 스택 → 그 안의 실행 컨텍스트 → 그 안의 환경 레코드.

그리고 Variable EnvironmentLexical Environment가 왜 둘인지를 짚은 게 이 글의 값어치입니다. 대부분의 자료는 둘을 나열만 하고 넘어가거든요. varlet의 스코프 차이를 지키기 위해서라는 답을 찾은 건, 실제로 궁금해했다는 뜻이에요.

참고 자료 세 개를 밝힌 것도 좋았습니다. 영상, 기술 블로그, 개인 정리를 두루 읽고 자기 순서로 재구성한 글입니다.

원문 읽으러 가기  ↗velog.io/@jong-kyung/execution-context
 

02

📊 Spring Boot Actuator, Grafana & Prometheus 모니터링 구축

오연수 님

오연수 님 원문에 실린 이미지
이런 내용이에요
돌아가고 있는 애플리케이션을 어떻게 지켜볼 것인가에 대한 글입니다.
Spring Boot Actuator가 무엇을 주는지, Micrometer가 어디에 끼는지, Prometheus가 어떻게 수집하는지, Grafana가 어떻게 그리는지를 네 조각으로 나눠 설명해요.
그리고 실제 구축 과정이 이어집니다. 의존성 추가, yml 설정, Spring Security 예외 처리, 멀티 모듈에서 어디에 넣을지까지요.

2기 내내 프레임워크 내부를 파고들던 그분이 3기에는 운영으로 넘어오셨습니다.

조금 더 들어가 보면

이 글이 잘한 건 네 도구의 역할을 명확히 갈라놓은 것입니다. 다이어그램이 전부를 말해줘요.

[Spring Boot App]
      │
      └── Actuator
            │
            └── Micrometer (메트릭 수집기)
                  │
                  └── Prometheus Registry (형식 맞춤)
                        ↓
[Prometheus 서버가 /actuator/prometheus 엔드포인트를 긁어서 수집]

각자가 딱 한 가지씩만 합니다.

· Actuator — 진단 엔드포인트를 열어줍니다. 다만 저장도 분석도 그래프도 안 합니다

· Micrometer — 실제로 수치를 모읍니다. 역시 수집만 하고 저장은 안 합니다

· micrometer-registry-prometheus — 그 수치를 Prometheus가 읽을 수 있는 형식으로 바꿔 내놓습니다

· Prometheus — 주기적으로 긁어가서 저장하고 쿼리(PromQL)할 수 있게 합니다

· Grafana — 저장된 데이터를 그래프로 그립니다

글이 각 단계마다 "➡️ 하지만 이건 안 함"을 붙여둔 게 좋습니다. 역할 경계가 선명해지거든요.

Prometheus가 "긁어간다(pull)"는 것도 짚어둘 만합니다. 애플리케이션이 데이터를 보내는 게 아니라, Prometheus가 주기적으로 찾아와 읽어갑니다. 그래서 애플리케이션 쪽은 그냥 엔드포인트만 열어두면 되고, 모니터링 서버가 죽어도 애플리케이션엔 영향이 없어요.

📝 원문에서는 이렇게 설정합니다

의존성 두 줄로 시작합니다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'io.micrometer:micrometer-registry-prometheus'
}

그리고 yml 설정이 두 개인데, 글이 이 둘의 차이를 정확히 갈라놓습니다.

management:
  endpoints:
    web:
      exposure:
        include: prometheus
"어떤 Actuator 엔드포인트를 HTTP로 외부에 노출할지 설정 (…) 즉, 엔드포인트를 열어주는 역할"
management:
  prometheus:
    metrics:
      export:
        enabled: true
"Prometheus 형식으로 메트릭 데이터를 만들도록 설정하는 역할"

"문을 여는 설정"과 "데이터를 만드는 설정" — 둘 다 있어야 동작합니다. 하나만 하면 문은 열렸는데 안에 아무것도 없거나, 데이터는 만들어졌는데 문이 잠긴 상태가 되죠. 설정이 왜 두 개인지 모르면 하나만 넣고 헤매기 쉬운 지점입니다.

그리고 실무에서 반드시 걸리는 부분도 짚습니다. "Spring Security 사용 시 /actuator/prometheus 경로를 허용해야 한다." 시큐리티가 붙어 있으면 Prometheus의 요청이 401로 막히거든요.

멀티 모듈에서 어디에 의존성을 넣을지도 명확합니다. @SpringBootApplication이 있는 모듈이요. 실제로 실행되는 주체여야 메트릭을 노출할 수 있으니까요.

🧭 그리고 무엇을 봐야 하는가

글이 정리한 주요 메트릭이 실전에서 그대로 쓰입니다.

메트릭 의미
jvm.memory.used JVM이 현재 사용 중인 메모리
jvm.gc.pause GC가 걸린 시간
process.cpu.usage 애플리케이션의 CPU 비율
http.server.requests 요청 수, 처리 시간, 상태 코드
logback.events 로그 이벤트 수 (info, error 등)

이 중 jvm.gc.pause가 특히 흥미롭습니다. 2회차에서 다뤘던 Stop-The-World를 실제로 재는 숫자거든요. "가끔 응답이 튄다"는 증상이 GC 때문인지 여기서 확인됩니다. 개념으로 알던 것을 측정 가능한 값으로 바꾼 셈이에요.

logback.events도 실용적입니다. 에러 로그 수가 갑자기 늘면 알림을 보내게 할 수 있죠. 사람이 로그를 계속 들여다보지 않아도 됩니다.

큐레이터 노트

"만드는 일"에서 "운영하는 일"로 넘어간 기록이라 뽑았습니다.

개발을 배울 때는 기능을 만드는 데 집중합니다. 그런데 서비스를 실제로 굴리기 시작하면 "지금 잘 돌고 있나"가 더 중요해져요. 메모리가 새고 있지는 않은지, 응답이 느려지고 있지는 않은지를 숫자로 볼 수 있어야 합니다.

그리고 이 글은 역할 경계를 정확히 그었습니다. Actuator·Micrometer·Prometheus·Grafana는 이름이 비슷비슷해서 처음엔 뭐가 뭔지 헷갈리는데, "각각 무엇을 하고 무엇을 안 하는지"를 나눠주니 구조가 한 번에 들어옵니다.

막히는 지점을 미리 적어둔 것도 좋았어요. 시큐리티 경로 허용, 멀티 모듈에서의 위치, 설정 두 개의 차이. 전부 실제로 해봐야 알 수 있는 것들입니다.

이 저자는 1기부터 3기까지 꾸준히 큐레이션되고 계세요. 스프링 내부 → 코틀린 컴파일 → 데이터 정합성 → 모니터링으로, 다루는 층이 계속 넓어지고 있습니다.

원문 읽으러 가기  ↗devlemon.tistory.com/30

💡 두 편을 겹쳐 읽으며

한 편은 자바스크립트 엔진 내부, 한 편은 서버 운영 도구입니다. 접점이 없어 보이죠.

그런데 두 글은 같은 것을 다릅니다. 둘 다 "지금 무슨 일이 일어나고 있는지 보는 방법"입니다. 다만 보는 위치가 정반대예요.

이종경 님은 안에서 봅니다. 코드 한 줄이 실행될 때 어떤 환경이 만들어지고, 변수가 어디에 저장되고, this가 무엇으로 정해지는지를요. 원리를 알면 결과를 예측할 수 있게 됩니다.

오연수 님은 밖에서 봅니다. 이미 돌아가고 있는 애플리케이션의 메모리, CPU, 요청 수를 숫자로 지켜보죠. 안을 몰라도 이상 징후를 알아챌 수 있게 됩니다.

둘 다 필요합니다. 그리고 실은 서로를 필요로 해요.

모니터링 그래프에서 메모리가 계속 우상향하는 걸 봤다고 합시다. 그래프는 "새고 있다"까지만 알려줍니다. 어디서 새는지는 안 나와요. 그때 클로저가 환경을 붙들고 있다는 걸 아는 사람이 원인을 찾습니다.

반대로 원리만 알고 관측이 없으면 문제가 생긴 줄도 모릅니다. 클로저를 잘 알아도, 실제로 어느 코드가 메모리를 붙들고 있는지는 재봐야 알거든요.

아는 것과 보는 것 — 둘이 만나야 고칠 수 있습니다.

이번 회차에서 가져갈 것

1. varlet은 저장소가 다르다. 함수 스코프(Variable Environment)와 블록 스코프(Lexical Environment)요. "var는 블록을 무시한다"의 이유가 여기 있습니다.

2. this는 호출 방식이 정한다. 선언된 위치가 아니라요. 단 화살표 함수만 예외로, 정의될 때 정해집니다. 객체 메서드를 화살표 함수로 쓰면 깨져요.

3. 클로저는 기능이자 누수 원인. 내부 함수가 살아 있으면 외부 환경 전체가 메모리에 남습니다. 안 쓰는 핸들러와 참조는 정리하세요.

4. 모니터링 설정은 두 개가 한 쌍. 엔드포인트를 여는 설정Prometheus 형식으로 만드는 설정이 따로입니다. 하나만 하면 동작하지 않아요.

5. jvm.gc.pause를 대시보드에 올려두기. "가끔 응답이 튄다"의 원인이 GC인지 아닌지가 이 숫자로 갈립니다. 개념을 측정값으로 바꿔두면 추측이 확인으로 바뀝니다.


좋은 글 남겨주신 이종경 님, 오연수 님께 감사드립니다! 😊

6회차 두 번째 큐레이션으로 이어집니다! 🌱

- 에디터 · 성장일지 큐레이터 -


※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.

WRITE YOUR GROWTH

오늘의 배움을
내일의 성장으로.

기록은 나를 돌아보게 하고, 공유는 우리를 연결합니다.
당신의 다음 성장 이야기도 그로스로그에서 시작해보세요.

커뮤니티 둘러보기