원문 링크 https://martinfowler.com/articles/2025-nature-abstraction.html 

 

LLMs bring new nature of abstraction

A long-form article entitled: "LLMs bring new nature of abstraction"

martinfowler.com

 

 이 바닥의 많은 떠벌이들과 마찬가지로, 나는 최근 생성형 AI  시스템이 소프트웨어 개발에 어떤 역할을 할 수 있을지에 대해 주목하고 있다. 내 생각엔 LLM의 등장이 가져올 변화는 어셈블리시절 고차원 프로그래밍언어(high-level programming languages)의 등장이 가저온 변화와 비슷할 것이라고 본다. 고차원 프로그래밍 언어와 프레임워크들은 우리의 추상화 레벨과 생산성을 향상시켰지만 프로그래밍의 본질에 대한 영향은 별로 없었다. LLM은 프로그래밍의 본질에 영향을 주고 있지만 단순히 추상화 수준을 끌어올리는 것에 그치지 않고 비결정론적(non-determinisitic)인 도구로 프로그래밍한다는 것이 무엇을 의미하는지에 대해서도 다시 한번 생각하게 만든다. 

 고차원 프로그래밍 언어들은 새로운 레벨의 추상화를 아주 빠르게 전달했다. 어셈블리 언어를 사용할 때는 특정 머신의 명령어 집합 만을 염두해두고 작업하게 되고 간단한 작업조차도 어떻게 하면 데이터를 올바른 레지스터에 등록해서 특정 행위를 할 수 있게 만들지에 대해 스스로 고민하고 파악해야 했지만. 고차원 언어에서는 명령문, 조건문을 통한분기, 데이터 컬렉션에 반복문을 돌리면서 작업을 수행하는 방법을 생각할 수 있게 되었다. 내의 코드에 이름(식별자)을 붙일 수 있게 되어서 각 값들이 무엇을 의미하는지 명확하게 표현할 수 있게 되었다. 초기의 언어들에는 명백한 한계가 있었다. 나의 첫 번째 프로그래밍 언어는 포트란4였다. 포트란4에는 'if'문에 'else'가 없었고, 정수형 변수를 선언할때는 변수명을 "I"부터 "N"사이의 문자로 시작해야 한다는 규칙이 있었다. 

 그러한 제약을 제거하고 블록 구조를 더했을 때(IF 문 이후에 한 줄 이상의 코드를 쓸 수 있게 되었다.) 프로그래밍은 쉬워지긴(그리고 더 재미있어졌다) 했으나 이러한 변화는 모두 같은 부류의 진보였다. 요즘 나는 반복문을 거의 사용하지 않는다. 나는 함수도 자연스럽게 data처럼 전달하지만 기계에게 명령을 내리는 방법은 포트란을 사용하던 시기와 크게 변하지 않았다. Ruby는 포트란과 비교하면 훨씬 정교하고 세련된 언어지만 포트란과 동일한 분위기를 가진다. 마치 포트란과 PDP-11 기계어 명령사이에서는 느낄 수 없는 분위기의 차이다.

 비록 지금까지 나는 최고의 생성형 AI 도구 들을 가볍게 만져보는 정도밖에 해보지 못했지만 친구들과 동료들의 AI 툴 경험을 듣는 것에 매료되어 있다. 나는 기계와 대화하는 방식이 루비에서 프롬프트로 변화된 것은 포트란과 어셈블리 언어 사이의 변화만큼 큰 변화라는 것을 확신한다. 추상화의 관점에서 보면 이는 더 큰 변화다. 내가 포트란 함수를 작성할 때, 나는 코드를 수백 번 컴파일을 할 수 있고 수백 번 컴파일해도 내가 작성한 코드의 결과는 변하지 않는다(버그가 있었다면 매번 같은 버그가 발 생한다). LLM은 확정적이지 않은 추상화를 가지고 왔다. 그 결과 내가 git에 동일한 프롬프트를 올려둔다고 해서 그 프롬프트를 실행한 결과가 매번 같지 않다. 나의 동료 브리기테가 말한 것처럼 우리는 추상 레벨을 위로 올리는 동시에 '비결정성'이라는 방향으로 옆으로 이동하고 있다. 

illustration: Birgitta Böckeler

 우리가 LLM을 업무에서 적용하는 방법을 배우는 것과 동시에 우리는 이 '비결정성'과 함께 살아가는 방법을 알아내야 한다. 이런 변화는 매우 극적이고 나를 흥분시키고 있다. 나는 어느 순간 우리가 잃게 될 것에 대한 아쉬움을 느낄 수도 있겠지만 대부분 이해하지 못한 새로운 것을 얻게 될 거란 사실도 알고 있다. 이런 '비결정성'의 진화는 아직까지 우리 업계 역사 속에서는 유래없는 진화이다. 

반응형

eventHandling

  • react의 component는 상태를 가지고, 상태의 변화에 따라 컴포넌트가 리 랜더링됨
    • 즉, 리엑트 컴포넌트의 상태를 변경시키는 무언가가 있다.
      • 그 무언거의 대부분이 바로 이벤트
  • javaScript의 event와 약간차이는 있으나 비슷하다.
    • react도 결국 javaScript다.
  • JSX에선 카멜케이스로 구성된 속성에 이벤트 함수를 넣는다.
    • <button onClick={handleClick}>Button </buttonn>

  • react의 이벤트에선 이벤트함수가 호출되면 인자로 SyntheticEvent 객체가 전달됨
    • SyntheticEvent는 javascript의 event 객체를 래핑한 객체임.
    • 거의 유사한 인터페이스를 가지고 있음
    • 원본 이벤트 객체도 접근 가능 SyntheticEvent.nativeEvent

eventHandler함수를 만들때는 react lifecycle을 고려해야한다.

  • 컴포넌트 내부에서 함수를 만들면 리랜더링 될때마다 새로 함수가 쓰여진다.
    • 이게 필요한 순간도 있겠으나 아닌 순간도 있다.
    • 새로 함수를 쓸 필요가 없다면 컴포넌트 외부에서 이벤트 핸들러 함수를 만들자.
    • 또는 useCallback으로 감싸주면 리랜더링을 피할 수 있다.
import { useCallback } from "react";

// 컴포넌트 외부에 함수 선언
function handleClickOutOfComponent(e) {
  console.log("click2", e);
}

function App() {
  // 컴포넌트 내부의 함수 
  function handleClick(e) {
    console.log("click1", e);
  }
  const handleChange = useCallback((e) => {
    console.log("change", e.target.value);
  }, []);

  return (
    <div>
      <button onClick={handleClick}>Button1</button>
      <button onClick={handleClickOutOfComponent}>Button2</button>
      <div>
        <input type="text" onChange={handleChange} />
      </div>
    </div>
  );
}
export default App;

이벤트의 종류

Mouse event

  • onClick : 마우스 클릭 (down + up )
  • onMouseDown : 버튼 누를시
  • onnMouseUp : 버튼 뗄시
  • onMouseEnter : 요소 밖에서 안으로 마우스 커서가 들어감
  • onMouseLeave : 요소 안에서 밖으로 마우스 커서가 나감
  • onMouseMove : 요소 안에서 마우스 커서가 움직임

Keyboard event

  • onKeyDown : 키를 눌렀을때 (물리적인 키에 반응)
  • onKeyUp : 키를 떼었을때
  • onKeyPress : 키를 눌러서 문자가 입력될때(눌린 문자에 반응함)

Focus, Form event

  • Focus
    • onFocus : 요소가 포커스 될때
    • onBlur : 요소의 포커스가 사라질경우
  • Form
    • onChange : 요소의 값이 변경될때

그외

  • 필요할때마자 찾아가면서 사용하도록하자.

Form 요소 컨트롤

  • Form 요소를 컨트롤할때는 이벤트를 자주 사용한다.

Controlled Component

  • 제어 컴포넌트 라고도 함.
  • React에 의해 입력 요소 값이 제어되는 컴포넌트
import { useState } from 'react';

function TextArea() {
  const [textAreaVal, setTextAreaVal] = useState('');

  console.log('[TextArea] render', value);

  return (
    <textarea
      value={textAreaVal}
      onChange={(e) => {
        setTextAreaVal(e.target.value);
      }}
    />
  );
}

export default TextArea;
  • 위 코드에서 textAreavalue 속성에 textAreaVal 스테이트 값을 넣어줌
  • onChange 에서는 setTextAreaVal를 호출함
  • 유저가 값을 입력하면 state에 유저의 입력이 반영 되면서 상태가 업데이트됨

장점

  • 컴포넌트의 state 와 input value가 완전히 동일함(신뢰가능한 단일 출처)
  • 다른 컴포넌트에 input value를 전달하거나 다른 이벤트 핸들러에서값을 재설정 할 수 있음

단점

  • 값이 변경되는 매 순간 렌더링이 됨
  • 컴포넌트의 영향 범위가 클수록 성능 저하됨

Uncontrolled Component

  • Controlled Component와는 반대로 input value를 컴포넌트 State에 연결하지 않고 사용하는 컴포넌트
  • 값은 dom을 통해 가져온다.
    • ex: document.querySelector('#{id}').value
  • useRef를 사용해 가져올 수 도 있다.
    • useRef는 Hooks의 한 종류. 특정 요소의 레퍼런스를 직접 가져올때 사용
    • 연결된 값이 변경되더라도 리랜더링이 되지 않는다.
import { useRef } from 'react';

function UncontrolledTextInput() {
  const inputRef = useRef(); //1. useRef를 이용해 ref를 만든다.

  console.log('[UncontrolledTextInput] render');

  return (
    <>
    /* jsx에서 ref속성에 연결한다.  */
      <input ref={inputRef} type="text" id="input" />
      <button
        onClick={() => {
          console.log(inputRef.current.value);
        }}
      >
        Get value
      </button>
    </>
  );
}

export default UncontrolledTextInput;

반응형

'프로그래밍 > react 학습노트' 카테고리의 다른 글

react 학습노트 | 3.Hooks  (0) 2025.04.20
react 학습노트 | 2.Component  (0) 2025.04.11
react 학습노트 | 1.JSX란?  (0) 2025.03.30

Hooks

  • react의 life cycle을 알려면 Hooks를 알 필요가 있다.
  • Hooks에 대해 알아보자.

Hooks의 종류

  • useState
  • useEffect
  • useCallback
  • useMemeo, useContext, useRef, useLayoutEffect
  • 위와 같이 10개 정도가 있는데, 그중 상위 3개정도가 주로 사용된다. 나머지는 필요할때 찾아서 쓰면된다.

useState

  • component의 State를 생성하는 Hook
    const [value, setValue] = useState(0) //인자는 초기값
    // 첫번째 요소(value) => state 값
    // 두번째 요소(setValue) => state를 set 해주는 setter 함수

useEffect

  • component가 랜더링 될때 현재 상태 변화에 따라 조건적으로 특정 작업을 실행하기 위한 Hook
    • 처음 랜더링 될때
    • 컴포넌트가 언마운트 될때
      • cleanup 함수를 이용해서 component unmount시 자원, 이벤트 등을 정리해준다.
        useEffect(() => {
        ...
        return () => {
        ... // cleanup 함수. component 가 unmount될때 실행됨
        }
        },[])
  • 상태가 변할때
    • useEffect()함수에 인자로 상태 변화를 감지할 state를 배열로 전달한다.
    • 이 경우 클린업 함수는 상태 변화를 감지하고 useEffect를 재실행하기 직전에 실행됨.
      const [value, setValue] = useState(0) 
      useEffect(() => {
      ...
      return () => {
      ... // cleanup 함수. component 가 unmount될때 실행됨
      }
      }, [value])

useCallback

  • 리 랜더링 시 기존에 만든 함수를 재생성 하지 않고 재활 용 하기 위해 사용하는 Hooks
  • memoization
const fooFun =useCallback(()=>{
  console.log("bar");
})

Hooks 사용시 주의사항

  • 조건적으로 사용하면 안된다.
  • Hooks 는 React Component에서만 사용 가능하다.

React의 렌더링 과정

Rerendering

  • Component의 상태(state, props)가 변경 되면서 해당 컴포넌트를 다시 실행하여 화면에 그린다는 의미.
  • Rerendering시 매번 다른 동작을 하게되고 이를 우리는 life cycle이라고함

React의 Life Cycle

  • Class형 과 Function형이 약간 다름
  • Class
    1. constructor 실행
      • 최초 실행
        1. getDerivedStateFromProps() 실행 : props로 넘어온 값을 state와 동기화하는 메소드
        2. render method 실행 : 화면에 그릴 JSX를 반환
        3. componentDidMount() 실
      • 업데이트 시
        1. getDerivedStateFromProps()
        2. shouldComponentUpdate() 실행 : 현재 일어난 상태변화를 확인하여 컴포넌트를 다시 랜더링 할지 말지를 결정
        3. componentDidUpdate()
      • 컴포넌트 삭제시
        1. 제거
        2. componentWillUnmount()실행
  • function형
    • 최초 실행
      1. 자기자신을 실행하여 JSX를 리턴하고 DOM에 반영
      2. useEffect Hook 실행: componentDidMount, componentDidUnMount, componentDidUpdate()를 커버해줌.
    • 업데이트시
      1. 자기 자신 실행 후 JSX반한
      2. useEffect 실행
    • unmount 시
      1. useEffect만 실행
반응형

'프로그래밍 > react 학습노트' 카테고리의 다른 글

react 학습노트 | 3.EventHandling  (0) 2025.04.26
react 학습노트 | 2.Component  (0) 2025.04.11
react 학습노트 | 1.JSX란?  (0) 2025.03.30

Component

  • 스스로 상태를 관리하는 캡슐화된 코드 조각
  • 컴포넌트는 하나의 JSX를 반환하는 함수.
  function App(){  
  return (
    <h1> Hello </h1>
    <h1> World </h1>
   )  
  }

  ReactDom.render(
    document.getElementById('root')  
  )

위 코드에서 App() 함수가 컴포넌트. div 태크로만들어진 JSX를 반환한다.
ReactDomrender 메소드를 이용해서 화면에 그린다.

JSX로 컴포넌트를 만드는것과 함수형태로 컴포넌트를 만드는 방식의차이

  1. 컴포넌트는 기본적으로 함수이기 때문에 자신만에 고유한 로직이 들거갈 수 있다.
  2. 스스로 상태를 가질 수 있다.

컴포넌트 생성시 주의사항

  • 컴포넌트 이름은 PascalCase
    • MyComponent
    • ArticlePage
    • UserProfile
  • 컴포넌트는 의미 단위로 쪼개서 파일을 분리한다.
    • header, content, footer ...
    • 각 컴포넌트가 역할을 가지도록한다.
  • 최 상위 컴포넌트는 일반적으로 App이다.
  • App컴포넌트를 ReactDom.render() 를 이용해서 랜더링 하는 코드는 일반적으로 index.js에 넣는다.
  • index.js 는 모든 컴포넌트의 엔트리 포인트, App.js는 모든 컴포넌트의 root 컴포넌드

Props

  • 프로퍼티의 줄임말.
  • 컴포넌트에서 하위 컴포넌트로 어떤 값을 전달해줄때 사용하는 요소
function App() { 
  return( 
    <div> <MyComponent value={'test'} /> </div> 
  ) 
} 

function MyComponent(props) { 
  return <div>{props.value} </div> 
}
  • App Component가 하위 컴포넌트인 MyComponent로 데이터를 전달해줌.
  • 전달반은 데이터는 object형태이며, 전달 했던 속성 이름으로 접근해서 사용할 수 있음.
    function App() { 
    return(
     <div>
       <MyComponent>
         <h1> value </h1> 
       </MyComponent>
     </div>
    )
    }


  function MyComponent(props) {  
    return
      {props.children}
    }
  • 상위 컴포넌트가 값을 감싸는 형태로 전달도 가능. 이때는 props에서 children을 이용해서 접근
  • 이때 children으로 전달받은 데이터는 <h1> 태그로 이루어진 JSX 값이다.

Props 활용팁.

  • 구조분해 할당을 잘 활용한다.
  • 특정 Props에 기본 값을 줄 수 있다.(defaultProps).
  • Props는 읽기 전용.

State

  • 컴포넌트 내부에서 사용되는 변수
    • state 값이 변하면 컴포넌트가 리렌더링됨.
    • 렌더링 사이클에서 값이 보존됨
function App(){ 
  const[value, setValue] = useState(); 
  return ( 
    <div> {value} </div>
  ) 
}
  • useState를 사용하면 value와 setValue를 리턴해준다.
  • setValue를 이용하면 value를 set할 수 있으며 이때 리 랜더링이 실행됨
  • value가 set 되는 시점은 리렌더링이 된 이후라는점에 주의해야함.

Hooks

  • 클래스형 컴포넌트의 단점을 보완하기 위해 함수형 컴포넌트에 지급된 개념
  • 지금까지는 함수형 컴포넌트를 사용했다.
  • 하지만 클래스형 컴포넌트라는 방식도 있다.
    • 과거에 useState 같은 Hooks는 이용할 수 없었기때문에 클래스형 컴포넌트를 이용했다.
    • 클래스형은 몇가지 안좋은점이 있어서 점점 안쓰게 되었다.
반응형

'프로그래밍 > react 학습노트' 카테고리의 다른 글

react 학습노트 | 3.EventHandling  (0) 2025.04.26
react 학습노트 | 3.Hooks  (0) 2025.04.20
react 학습노트 | 1.JSX란?  (0) 2025.03.30

JSX란?

  • Javascript 확장 문법으로 Javascript XML이란 의미.

  • 리엑트에서는 JSX를 이용하여 화면(컴포넌트)을 표시함.

    • 리엑트가 JSX -> HTML을 만들어줌

    JSX의 특징

    • JSX에서 사용되는 태그의 속성 이름이 HTML과 약간 다름
      • class => className
      • for => htmlFor
      • onclick => onClick
    • 태그를 명시적으로 닫아줘야함.
        //잘못된 JSX
        <button class="btn"> hello, world </button>
        <input type="text"> 

        //옳게된 JSX\`\`\` - 하나의 태그로 감싸져 있어야함.
        <div>
          <button className="btn"> hello, world </button>
          <input type="text"/> 
        </div>

JSX에서 Javascript사용하기

1. 중괄호 {} 안에서 JavaScript 표현식 사용

JSX 안에서는 중괄호 **`` 안에 JavaScript 표현식**을 사용할 수 있습니다.

const name = "Alice";

return <h1>Hello, {name}!</h1>;
  • ✅ 사용 가능한 것: 변수, 함수 호출, 삼항 연산자 등 표현식(expression)
  • ❌ 사용 불가: if, for 같은 문장(statement)

2. 조건부 렌더링

2-1. 삼항 연산자

const isLoggedIn = true;

return <div>{isLoggedIn ? "Welcome!" : "Please log in."}</div>;

2-2. 논리 AND (&&)

const hasMessages = true;

return <div>{hasMessages && <p>You have new messages.</p>}</div>;

3. 리스트 렌더링 (map 사용)

const items = ["Apple", "Banana", "Orange"];

return (
  <ul>
    {items.map((item, index) => (
      <li key={index}>{item}</li>
    ))}
  </ul>
);
  • key 속성은 필수는 아니지만 React가 효율적으로 렌더링하기 위해 사용합니다.

4. 반복문 사용하기 (for, while 등)

JSX 안에서는 for, while 같은 문장(statement) 은 직접 사용할 수 없습니다. 대신 반복문은 JSX 바깥에서 데이터를 처리한 뒤 JSX에서는 map() 등으로 표현식 형태로 렌더링합니다.

const items = ["Apple", "Banana", "Orange"];
const elements = [];

for (let i = 0; i < items.length; i++) {
  elements.push(<li key={i}>{items[i]}</li>);
}

return <ul>{elements}</ul>;
  • 또는 map() 사용이 가장 권장되는 방식입니다 (위의 리스트 렌더링 참고).

5. 함수 호출

function formatName(user) {
  return `${user.firstName} ${user.lastName}`;
}

const user = { firstName: "John", lastName: "Doe" };

return <h1>Hello, {formatName(user)}!</h1>;

6. 속성(prop)에도 JavaScript 표현식 사용 가능

const isDisabled = true;

return <button disabled={isDisabled}>Click me</button>;

7. 스타일 객체도 JavaScript로 정의 가능

const style = {
  color: "blue",
  fontSize: 20
};

return <p style={style}>Styled Text</p>;
  • CSS 속성 이름은 camelCase로 써야 합니다 (font-sizefontSize)

반응형

'프로그래밍 > react 학습노트' 카테고리의 다른 글

react 학습노트 | 3.EventHandling  (0) 2025.04.26
react 학습노트 | 3.Hooks  (0) 2025.04.20
react 학습노트 | 2.Component  (0) 2025.04.11

본 리뷰는 인사이트 에서 도서를 제공받은 후 작성한 리뷰입니다.

많은 Java 개발자들이 Kotlin을 배우려고하고 사용하려고합니다. 
그리고 그들중 대부분은 Kotlin을 조금 더 간결한 Java처럼 사용합니다. immutable변수나 타입 추론 등을 사용하면서 코틀린이 좋다 라고 말하곤 하죠.(네, 제 이야기였습니다) 

그렇게 되는 근본적인 이유는 무엇일까요? Java를 사용하는 백엔드 개발자들은 대부분 스프링 부트를 사용하고, 코틀린역시 스프링 부트 환경에서 사용하는 일이 많습니다. 그때문에 익숙한 환경에서 익숙한 방식으로 개발하게 되는 것 이겠죠. 적어도 저는 그랬습니다.

보통 코틀린을 시작할때는 코틀린 입문 서적 같은 책을 한권 읽어보고, 코틀린 언어의 특징 들을 살펴봅니다. 그러면서 이뮤터블 이라던가 델리게이션이라던가 코루틴이라던가 하는 코틀린의 특징에 대해 조금 알게되고 프로젝트를 시작하게 되죠.
이미 경험 많은 개발자가 함께하는 프로젝트가 아니라면 앞서 말씀드린것과 같이 Java 스러운 Kotlin코드를 짜게되기 마련입니다. 왜냐면 본인이 가장 잘 하던 방법을 사용하게 되고, 코틀린은 Java 스러운 코드도 훌륭하게 돌려주거든요. 하지만 이내 곧 깨닫게 됩니다. '나는 문법만 코틀린을 쓰고, 코틀린 라이브러리가 제공해주는 조금 더 편리한 기능만 쓰고있고 코틀린의 핵심적인 기능은 안쓰고 있다' 라는 사실을요. 

대부분의 코틀린 기본서적에서 코틀린의 특징을 잘 잡아주지만 그것을 잘 이해하고, 사용하게 할 만큼의 정보를 제공해주진 않습니다. 코틀린은 그만큼 다양한 기능이 많이 있어서 아마 자세히 설명 하자면 책이 3권쯤은 필요할거같습니다.

그런데 그것을 실재로 해낸 책이 있습니다. 바로 '코틀린 아카데미' 시리즈죠. 그중 제가 읽을 코틀린 아카데미 - 고급편은 코틀린이 그토록 자랑했지만 실제 적용하긴 어려웠던 내용들을 차근차근 설명 해 주고 있습니다. 특히 1장 Variance에서 JVM의 Type Erase 때문에 발생하는 문제를 Kotlin이 어떻게 처리하는지 알려줍니다. 1장만 보더라도 기존에 'in을 쓰니까 빨간줄이 사라지는구나'라고 생각하던 개발자들에겐 크게 도움이 될 것입니다. 그 외에도 델리게이션을 왜, 어떻게 쓰는지에 대해 자세히 설명 해 주고 Contract가 어떻게 컴파일러에게 코드의 제약을 설명해 주는지 알려줍니다. 또, Java 와의 상호운용성을 심도있게 다루면서 JVM에서 코틀린을 어떻게 사용해야하는지 가이드해줍니다. 코틀린 멀티플랫폼과 Kotlin/JS 부분도 제가 평소에 관심을 가지는 부분이라 재밌게 읽었습니다. 특히 이 두 부분은 그동안 '가능하긴 하지만 아직 쓸만하려면 멀었다' 라는 기존의 평가에 대해 다시 한 번 생각하게 만들어줬습니다. 그 외에도 Reflection, Annotation Processor, Kotlin Symbol Processor ,Kotlin Compiler Plugin 역시 자세히 다루고 있습니다. 

일부 기능은 저같은 웹개발자들은 크게 관심이 없을 수 있는 기능이기도하고, 프레임워크를 만드는 수준의 개발이 아니라면 꼭 알아야 하는 지식은 아닐 수 있으나 코틀린이란 언어에 대해 조금 더 알 수 있는 계기가 될 수 있으니 책을 1회정도는 가볍게 끝까지 소설 읽듯이 읽어보시고 필요한 부분들을 다시한번 공부하듯이 읽어보시기를 추천합니다.

 

반응형

Connection Pool 크기와 타임아웃 설정

connection pool

기본개념

1. Connection Pool의 역할

  • Connection Pool은 애플리케이션이 데이터 베이스와 통신하기 위해 미리 연결을 생성 해 두는 저장소.
  • 연결을 매번 생성/해제 하면 성능이 저하되므로 재사용 가능한 연결 풀을 통해 성능을 최적화한다.

2. 주요 파라미터

  • Maximum Pool Size: 동시에 사용할 수 있는 최대 연결 수.
  • Minium Idle Connections: 사용되지 않을 때 유지할 최소 연결 수.
  • Connection Timeout: 연결 요청 시 대기 할 최대 시간.

Connection Pool 크기 설정

왜 중요하가?

  • 연결 풀 크기가 너무 작으면 요청이 대기 상태가 되어 처리 속도가 느려짐.
  • 연결 풀 크기가 너무 크면 DB서버에 과부하가 발생하여 성능이 저하된다.

단계별 설정

  1. 트래픽 분석
    • 애플리케이션의 동시 사용자 수와 요청 패턴을 분석.
    • ex: 애플리케이션 초당 평균 100개의 요청이 발생하고 각 요청이 50ms동안 연결을 유지한다
      = 100 * 0.05
      = 5(최소 연결 필요) 
  2. DB서버의 제한 확인
    • DB서버가 지원할 수 있는 최대 연결 수를 확인.
    • ex: MySQL SHOW VARIABLES LIKE 'max_connections' ;
  3. 기본 설정
    • 최소 연결 수는 트래픽 분석 결과의 80%로 설정
    • 최대 연결 수는 DB 서버의 최대 연결 수의 80%를 넘지 않도록 설정.
  4. 로드 테스트
    • JMeter, Gating 등의 도구로 예상 트래픽을 시뮬레이션 하여 성능 확인.
    • 연결 풀 부족 시 대기시간이 늘어나는지 확인.

Connection Timeout 설정

  1. 왜 중요한가?
    • 타임아웃을 짧게 설정하면 불필요한 대기 없이 오류를 빠르게 감지할 수 있다.
    • 너무 길게 설정하면 장애 원인 파악이 어려워진다.
    • 너무 짧게 설정하면
      • 네트워크가 느리거나 서버가 과부하일 경우 요청이 처리되기 전 연결이 끊어질 수 있다.
      • 재시도 과정에서 네트워크 대역폭과 서버리소스가 불필요하게 소비된다.
  2. 설정 단계
    1. 요청 응답시간 확인
      • 평균적 DB응답 시간은 수십ms에서 수백ms 수준.
      • ex: 쿼리가 200ms 내외로 완료된다면 타임아웃을 500ms ~ 5000ms 로 설정
    2. Connection Timeout 설정
      • HicariCP의 connection-timeout은 견결을 기다릴 최대 시간이다. (default: 3000ms)
      • 실무에서는 보통 2000~10000ms 사이로 설정한다.
    3. Validation Timeout 추가.
      • 연결 유효성을 검사하는 시간. 일반적으로 1000~5000ms 설정
    4. 실패시 재시도정책
      • 타임아웃 발생 시 재시도를 설정하거나 Circuit Breaker를 사용한다.

성능 최적화

  1. 모니터링 도구를 활용한다.
    • Actuator의 /metrics 엔드포인트에서 hicaricp.connections.*지표를 확인.
    • 그라파나, 프로메테우스와 통합하여 커넥션 풀 상태 모니터링
  2. 쿼리 효율성 확인.
    • 슬로우쿼리가 풀을 점유하지 않도록 주기적으로 쿼리 성능 분석.
    • mysql의 경우 slow_query_log 활성화
  3. 스케일링 전략
    • 트래픽이 지속적으로 증가할 경우 DB와 애플리케이션 인스턴스 수를 증가시킨다.
반응형

 

JVM의 Garbage Collection(GC)

  • Garbage Collection(GC)은 JVM(Java Virtual Machine)에서 동정으로 생성된 객체의 메모리를 자동으로 관리하는 메커니즘. GC는 애플리케이션의 메모리 누수를 방지하고, 더 이상 사용되지 않는 객체를 제거해 메모리를 효율적으로 사용하도록한다.

기본개념

1. GC의 역할

  • JVM이 힙 메모리(Heap Memory)에 생성된 객체 중 더 이상 사용되지 않는 객체를 식별하고 제거한다.
  • 이를 통해 애플리케이션의 메모리 누수를 방지하고, 수동 메모리 관리의 필요성을 제거한다.

2. GC가 관리하는 영역

  • Heap Memory
    • Youg Generation (Eden, Survivor Space)
    • Old Generation (Tenured)
  • Metaspace
    • 클래스 메타데이터를 저장하는 공간.

JVM Heap Memory 구조

1. Young Generation

  • 새로 생성된 객체가 저장됨.
  • 크기가 작고, 객체의 생존 시간이 짧은 경우가 대부분.
  • 구성요소
    • Eden Space: 새로운 객체가 처음 생성되는 공간.
    • Survivor Space: Eden에서 살아남은 객체가 복사되는 공간.

2. Old Generation

  • Young Generation에서 오래 살아남은 객체가 이동.
  • 크기가 크거나 생존 시간이 긴 객체가 저장됨.

3. Metaspace

  • 클레스 로더와 메타데이터 정보를 저장.

GC 동작 과정

1. Minor GC

  • Young Generation에서 발생.
  • 동작 원리
    1. Eden에서 객체가 생성.
    2. Eden이 가득차면 Minor GC가 실행
    3. Eden에서 살아남은 객체가 Survivor Space로 이동.
    4. 여러번의 Minor Gc를 통해 Survivor Space에서 생존한 객체가 Old Generation으로 이동.
  • 특징
    • 실행 속도가 빠르며, Young Generation 크기에 다라 성능이 좌우됨.
    • Stop-The-World 발생

2. Major GC(Full GC)

  • Old Generation에서 발생
  • 동작 원리
    1. Old Generation이 가득 차면 실행
    2. 사용되지 않는 객체를 제거하고 힙 메모리를 정리
  • 특징
    • 실행 시간이 길고 Stop-The-Wold가 발생
    • Minor GC보다 성능에 더 큰 영향을 미침.

3. GC의 실행 과정(가비지 식별, 처리 과정)

  • Mark: GC는 모든 객체를 순회하며 현재 참조되고 있는 객체를 식별하고 마크
  • Sweep: 마크되지 않은, 즉 참조되지 않는 객체를 메모리에서 제거함.
  • Compact(압축): 객체를 제거하고 남은 메모리 조각들을 모아 연속된 공간을 확보함. 힙메모리 단편화를 해소하는데 도움을 줌.

GC의 종류

JVM은 다양한 GC 알고리즘을 제공한다. 각각의 알고리즘은 GC성능과 애플리케이션 요구사항에 따라 다르다.

1. Serial GC

  • 단일 스레드에서 작동
  • 특징
    • 작은 애플리케이션(싱글 스레드)에 적합.
    • Full GC중 애플리케이션이 멈추는 "Stop-The-Wold"시간이 길다.

2. Parallel Gc(Throughput GC)

  • 여러 스레드를 사용해 GC를 수행
  • 특징
    • 처리량(Throughput) 지향.
    • 적절한 애플리케이션 성능과 낮은 GC비용을 제공.

3.G1 GC

  • Java 9 이후의 기본 GC
  • 특징
    • 힙을 Region으로 나누어 관리.
    • Yong/Old Generation을 따로 구분하지 않고 필요에 따라 Region을 재배치.
    • 짧은 응답 시간과 적은 Stop-The-World 시간을 제공.

4. ZGC

  • 매우 낮은 지연시간을 목표로 설계
  • 특징
    • 대규모 힙을 지원 (최대 16TB)
    • 애플리케이션 중단 시간이 10ms 이내

5. Shenandoah GC

  • 낮은 지연시간을 제공.
  • 특징
    • GC 작업과 애플리케이션 작업을 병렬로 수행.
    • ZGC 보다 짧은 지연시간 제공.

GC 튜닝과 모니터링

1. JVM옵선을 통한 설정

  1. GC 종류 선택
    • -XX:+Use{GCNAME}
    • ex: G1GC -XX:+UseG1GC
  2. Heap 크기 설정
    • Xms: 초기 힙 크기 (ex: Xms512m)
    • Xmx: 최대 힙 크기 (ex: Xmx1024m)
  3. GC로그 활성화
    • -Xlog:gc

2. 주요 GC메트릭 모니터링

  • GC 횟수와 시간: 애플리케이션 성능에 얼마나 영향을 주는지 확인.
  • 힙 사용량: Minor GC및 Major GC 후의 메모리 상태
  • 툴 사용: VisualVM, JConsole, Prometheus && Grafana

3.최적화를 위한 설정

  1. 적절한 Heap크기 설정
    • 너무 작은 힙 크기는 잦은 GC유발
    • 너무 큰 힙 크기는 GC시간이 오래걸림
  2. 객체 생명 주기 관리
    • 불필요한 객체 생성 줄이기
    • 전역 변수 사용 최소화
  3. GC 로그를 통한 분석
    • GC로그를 주기적으로 분석 하여 병목현상을 파악.
    • 애플리케이션 패턴에 따라 GC알고리즘 선택
  4. 메모리 누수 감지
    • 정적 코드 분석 툴을 사용하여 메모리 누수 방지.
반응형

JVM 메모리 구조에 대해 설명해보자

이미지는 JVM Memory 구조와 관련이 없습니다.

  • JVM 메모리 구조는 JVM에서 프로그램이 실행될 때 데이터를 효율적으로 관리하기 위해 나누어진 영억이다. 각 영역은 특정한 역할과 특징을 가지마 애플리케이션의 안정적 실행을 보장한다.

1. Method Area

  • 메소드 영역은 클래스와 관련된 메타데이터, 즉 프로그램의 구조적 정보를 저장하는 영역이다.
  • JVM이 실행되는 동안 공유 메모리 영역으로 사용된다.
  • 저장 내용
    • 클래스 메타데이터: 클래스 이름, 부모 클래스 정보, 인터페이스, 메소드, 필드 등.
    • Static 변수: 클래스 로드 시 초기화되는 정적 변수.
    • Constant Pool(상수 풀): 문자열 상수나 메소드/필드 참조와 같은 상수 정보를 저장.
    • 바이트코드: 각 메소드의 실제 실행 코드.
  • 특징
    • 모든 Thread에서 공유함.
    • JVM이 시작될 때 초기화되고, 종료될 때까지 유지된다.

2. Heap Area

  • 객체와 배열이 동적으로 생성되고 저장되는 메모리 영역.
  • JVM에서 가장 큰 메모리 영역. Garbage Collector에 의해 관리됨.
  • 저장 내용
    • 객체: new 키워드로 생성된 객체.
    • 인스턴스 변수: 객체가 가지고 있는 멤버 변수.
    • 클래스읜 런타임 데이터
  • 구조
    • 힙은 크게 Young GenerationOld Generation 으로 나뉜다.
      1. Young Generation
        • 새로 생성된 객체가 저장되는 영역.
      2. Old Generation
        • Young Generation에서 오래 살아남은 객체가 이동됨.
        • 메모리에서 가장 크고, 주로 긴 생명주기를 가진 객체가 저장됨.
      3. Metaspace(Optional)
        • Java 8 이후 부터 클래스 메타데이터를 관리하는 메모리 공간.
  • 특징
    • 모든 Thread에서 공유함.
    • 메모리가 부족하면 Garbage Collection이 실행됨

3.Stack Area

  • 각 쓰레드마다 독립적으로 생성되는 메모리 영역, 메소드 호출과 관련된 데이터를 관리함.
  • 저장 내용
    • Stack Frame: 메소드 호출 시마다 생성되는 단위.
      • 지역변수: 메소드 내에서 선언된 변수.
      • 매개변수: 메소드 호출 시 전달된 값.
      • 연산 중간 값: 연산 결과 저장.
  • 구조
    • LIFO(Last In, First Out)로 작동
    • 메소드 호출 시 스택 프레임 생성 -> 메소드 종료 시 제거.
  • 특징
    • 각 Thread 마다 독립적으로 생성.
    • 메모리 할당과 해제가 빠름.

4.PC Register

  • 현재 실행 중인 명령어의 조소를 저장하는 메모리 영역
  • JVM의 모든 명령은 순차적으로 실행되기 때문에 이 정보가 필요함
  • 저장 내용
    • 명령어 주소 : 현재 실행중인 JVM 명령어의 메모리 위치
  • 특징
    • 각 Thread 마다 독립적으로 생성
    • Java code 와 Native code 모두에 적용됨.

5. Native Method Stack

  • Java 코드가 아닌, JVM 이 실행하는 네이티브 코드(C, C++)와 관련된 메모리를 관리.
  • JNI(Java Native Interface)를 통해 호출되는 네이티브 메소드에서 사용.
  • 저장 내용
    • 네이티브 메소드의 호출 정보와 실행에 필요한 데이터.
  • 특징
    • 각 쓰레드마다 독립적으로 생성

정리

  1. Method Area: 클래스 메타데이터, static 변수 저장
  2. Heap Area: 객체와 인스턴스 변수 저장
  3. Stack Area: 메소드 호출과 관련된 데이터 저장
  4. PC Register: 현재 실행중인 명령어의 주소 저장.
  5. Native Mtehod Stack: 네이티브 코드 실행과 관련된 메모리.
반응형

[Kotlin] data class와 주의사항


데이타 클래스(data class)란

  • Kotlin에서 데이터를 저장하는 목적으로 사용되는 특별한 종류의 클래스. 데이터 클래스는 일반 클래스와 달리 몇가지 유용한 기능을 자동으로 제공함.

데이타 클래스의 특징

  1. 간단한 선언: data 키워드를 사용하여 쉽게 선언

    data class Person(val name: String, val age: Int) 
  2. 자동 생성 메소드: 컴파일러가 아래의 메소드를 자동으로 생성

    • toString()
    • equals()
    • hashcode()
    • componentN() 함수들
  3. 불변성: 주 생성자의 파라미터를 val로 선언하여 불변 객체를 만들 수 있음

    • var로 선언하는 경우 해시 기반 컬랙션에서 예상치 못한 동작이 발생할 수 있다.
  4. Destructuring (구조분해): 데이터 클래스의 속성을 쉽게 분해할 수 있다.

    val persion = Person("Alice", 30)
    val (name, age) = person 
    

data class 사용 예시

data class User(val name: String, val id: Int) 

fun main(){ 
  val user1 = User("John", 1) 
  val user2 = User("John", 1) 

  println(user1) // User(name=John, id=1)
  println(user1 == user2) //true

  val user3 = user1.copy(name = "Jane")
  println(user3) // User(name=Jane, id=1) 

user1 == user2 에서 자동 생성된 equals method 가 사용 되는데, 이때 객체 비교를 메모리 주소로 하는것이 아니라 을 기준으로한다.


data class의 제한 사항

  • Primary Constructor(주 새성자)는 최소 하나의 파라미터를 가져야한다.
  • 모든 Primary Constructor는 val 또는 var로 표시되어야한다.
  • data class는 abstract, sealed, inner class가 될 수 없다.

data class가 자동 생성하는 메소드

toString()

  • data class는 모든 프로퍼티를 포함하는 가독성 좋은 문자열 표현을 제공한다.
  • Primary Constructor에 정의된 프로퍼티를 표시해준다.
  • 필요하다면 override해서 사용할 수 있다.
data class User(val name:String, val age: 25) 

val user1 = User("Alice", 25)
println(user1.toString()) 
// output: User(name=Alice, age=25) 

hashCode()

  • Primary Constructor에 정의된 모든 프로퍼티를 사용하여 해시코드를 계산한다.
  • 필요한 경우 override해서 커스터 마이징 할 수 있다. 하지만 이 경우 equals() 메소드와의 일관성 유지가 필요하다.

  val user1 = User("John", 1) 
  val user2 = User("John", 1)
  val user3 = User("Alice", 2) 

  println(user1.hashCode()) // ex: 421124123
  println(user2.hashCode()) // ex: 421124123 user1과 같은 값 
  println(user3.hashCode()) // ex: -127486178 user1과 다른  값 

  val userSet = hashSetOf(user1, user2, user3)
  println(userSet.size) // 2 user1과 user2는 동일한 것으로 간주됨

equals()

  • 구조적 동등성을 판단한다 : 객체의 내용을 비교하여 동등성을 판단한다. 모든 프로퍼티의 값이 같으면 동등하다고 간주한다.
  • Primary Constructor에 정의된 모든 프로퍼티를 비교에 사용한다.
  • type safe : 비교하는 객체의 타입을 먼저 확인한다.
  • null safe : null에 대한 안전한 비교를 수행한다.
  • hashCode()와 일관성: equals()가 true르 반환하는 두 객체는 반드시 같은 hashCode()를 가져야한다.
  val person1 = Person("Alice", 30)
  val person2 = Person("Alice", 30)
  val person3 = Person("Bob", 25)

  println(person1 == person2) // true
  println(person1 == person3) // false
  println(person1.equals(person2)) // true
  println(person1.equals(person3)) // false

copy()

  • 불변성 유지: 원본 객체를 변경하지 않고 새로운 객체를 생성.
  • 선택적 파라미터: 변경하고자 하는 속성만 지정할 수 있음
  • type safe: 컴파일시 타입체크가 이루어짐
  • 모든 프로퍼티 복사 : 명시적으로 변경하지 않은 프로퍼티는 원본 객체의 값을 그대로 유지
  data class Person(val name: String, val age: Int, val city: String)

  val alice = Person("Alice", 30, "New York")

  // 나이만 변경
  val olderAlice = alice.copy(age = 31)
  println(olderAlice) // Person(name=Alice, age=31, city=New York)

  // 도시만 변경
  val aliceInLondon = alice.copy(city = "London")
  println(aliceInLondon) // Person(name=Alice, age=30, city=London)

  // 여러 속성 변경
  val bobInParis = alice.copy(name = "Bob", age = 25, city = "Paris")
  println(bobInParis) // Person(name=Bob, age=25, city=Paris)

copy의 주의사항

  • copy():는 shallow copy를 수행한다. 중첩된 객체나 컬랙션은 참조만 복사된다.

  • 복사를 너무 많이하면 성능에 악영향이 있을 수 있다.

  • 변경되지 않은 값은 그대로 사용한다.

    • 파라미터로 전달된 값만을 변경하고, 변경되지 않은 필드는 원본 객체 값을 그대로 사용한다.
    • 이 방식으로 불필요한 객체 생성 작없을 회피한다.
      data class User(val name: String, val age: Int)
    
      val user1 = User("Alice", 25)
      val user2 = user1.copy(age = 30)
    
      println(user1.name === user2.name) // true, name 필드는 그대로 재사용
    
      data class LargeData(val id: Int, val name: String, val description: String)
    
      val large1 = LargeData(1, "Test", "This is a large data object.")
      val large2 = large1.copy(description = "Updated description.")
    
      println(large1.id === large2.id)        // true, id 필드는 그대로 재사용
      println(large1.name === large2.name)    // true, name 필드도 그대로 재사용
      println(large1.description === large2.description) // false, 변경된 description만 새로 생성

    `* == 값 동등성 비교 / === 참조 동등성 비교

반응형

'웹프로그래밍 > Kotlin' 카테고리의 다른 글

[KOTLIN] CLASS  (0) 2023.02.25
[코틀린 KOTLIN 정리]01.코틀린 시작하기  (0) 2021.06.02
[코틀린KOTLIN 정리]0. 들어가며  (0) 2021.06.02

+ Recent posts