기본 콘텐츠로 건너뛰기

C#의 Boxing / Unboxing 정리

 


1. 박싱(Boxing)이란?

**박싱(Boxing)**은 **값 타입(Value Type)을 참조 타입(Reference Type)**인 object 또는 인터페이스 타입으로 변환하는 과정입니다.

1.1 왜 박싱이 일어날까?

  • C#에서 값 타입(예: int, float, struct)은 스택(Stack)에 할당되고, 참조 타입(예: class, object, string)은 힙(Heap)에 할당됩니다.
  • 어떤 이유로든 값 타입을 object나 인터페이스로 다뤄야 하는 상황이 생기면, .NET은 내부적으로 그 값을 힙 영역에 새롭게 복사(메모리 박스)하여 참조를 만들고, 이 참조를 반환합니다.
    • 예: int i = 123; object o = i; → i는 힙 메모리에 들어가 있는 박스화된 123을 가리키는 object가 됩니다.

1.2 박싱 예시 코드

int num = 42;          // 값 타입
object boxedNum = num; // 박싱 발생: int -> object
  • num은 스택 영역에 42를 담고 있음
  • boxedNum을 만들 때 힙에 새로운 공간이 생기고, 42가 그 공간에 복사됨
  • boxedNum은 object로서 해당 힙 객체(‘박스’)를 가리킵니다.

2. 언박싱(Unboxing)이란?

**언박싱(Unboxing)**은 박싱된 객체(참조 타입)에서 다시 값 타입을 꺼내오는 과정입니다.

  • 박싱의 반대 작업으로, object 변수에 들어 있는 박싱된 값을 다시 원래의 값 타입으로 변환해야 할 때 일어납니다.

2.1 언박싱 예시 코드

object boxedNum = 42; // 박싱
int unboxedNum = (int)boxedNum; // 언박싱
  • boxedNum은 힙 메모리에 박스된 42를 가리키는 object
  • (int)boxedNum을 통해 힙에 저장된 값을 다시 스택으로 복사해서 unboxedNum 변수에 할당

2.2 주의사항: 캐스팅 오류

object boxed = 42;    // int를 박싱
short s = (short)boxed; // InvalidCastException 발생
  • boxed 안에 실제로는 int 형식의 값이 들어 있으므로, short로 언박싱하면 예외가 발생합니다.
  • 언박싱은 원래 박싱했던 타입(또는 호환되는 형식)으로만 가능합니다.

3. 박싱/언박싱 시 성능 이슈

박싱과 언박싱은 다음과 같은 추가 비용이 발생합니다.

  1. 힙 할당/해제 오버헤드
    • 박싱 시 힙에 새로운 객체가 생성되고, 가비지 컬렉터가 이를 추적해야 합니다.
    • 언박싱도 힙에서 값을 다시 읽어 스택에 복사하는 과정을 거칩니다.
  2. 런타임 타입 체크
    • 언박싱 시점에, 실제 힙에 들어 있는 타입이 캐스팅 대상과 맞는지 확인합니다.

Tip: 박싱과 언박싱이 빈번하게 일어나면 GC(가비지 컬렉션) 부담이 커져 성능 저하가 생길 수 있습니다. 특히, 성능이 중요한 루프나 실시간 작업에서 주의가 필요합니다.


4. 박싱/언박싱을 피하는 방법

  1. 제너릭(Generic) 활용
    • 예: List<int> 대신 과거 C# 1.0 시절에는 ArrayList(object 기반)를 많이 썼고, int를 저장할 때마다 박싱이 일어났습니다.
    • 제너릭 컬렉션을 사용하면 타입에 안전하며 박싱/언박싱 없는 원시 값 타입의 저장이 가능합니다.
    // 구버전, 비권장: ArrayList 사용
    ArrayList arr = new ArrayList();
    arr.Add(10); // 박싱 발생
    int val = (int)arr[0]; // 언박싱
    
    // 제너릭, 권장: List<int> 사용
    List<int> list = new List<int>();
    list.Add(10);   // 박싱 없음
    int val2 = list[0]; // 언박싱 없음
    
  2. string.Format, Console.WriteLine 등에 매개변수 타입 주의
    • string.Format("{0}", someInt) → 내부적으로 object 파라미터를 받으므로 박싱 발생 가능
    • C# 6.0 이상에서 string interpolation($"Value: {someInt}")을 사용해도 내부적으로는 박싱이 일어날 수도 있습니다(특히 구조체나 IFormattable 등).
    • 꼭 필요하다면 ToString() 오버라이드를 사용하거나 IFormattable을 적절히 구현하여 박싱 없이 처리되도록 할 수 있습니다(고급 주제).
  3. Enum의 박싱 방지
    • 열거형(Enums)을 object로 다루거나, 인터페이스 파라미터에 전달하면 박싱이 발생
    • 필요하면 Convert.ToInt32(enumValue)처럼 정수 변환을 직접 명시하여 처리할 수 있습니다.
  4. boxing-free API 활용
    • 일부 .NET API는 오버로드로 object를 받지 않고 제너릭 메서드를 제공해 박싱을 피할 수 있게 해줍니다.
    • 예: string.Join<T>(), StringBuilder.Append<T>() (일부 버전부터 지원) 등

5. 실제 사례

5.1 수많은 박싱이 발생하는 코드

// 예: 대규모 로그 시스템 - 다양한 숫자 타입 로그를 남김
public void LogValues(params object[] values)
{
    foreach(var v in values)
    {
        Console.WriteLine(v);
    }
}

// 사용 예시
for(int i = 0; i < 10000; i++)
{
    LogValues(i, i*2, i*3.14f);
}
  • LogValues(int, int, float) → 모두 object 파라미터 배열로 변환 (박싱)
  • 반복문이 1만 번 도니까, 박싱 연산이 3만 번 발생
  • 이로 인해 GC 빈번 호출 가능, 성능 저하 위험

개선 방안

  • string 형태로 미리 변환(.ToString())해서 넘기거나, 제너릭이나 별도 오버로드를 활용해 object가 아닌 타입으로 받는 방법 고려

6. 정리

**박싱(Boxing)/언박싱(Unboxing)**은 값 타입과 참조 타입 간 변환에서 자연스럽게 일어나는 동작이지만,

  • 무분별하게 사용하면 불필요한 힙 할당과 성능 저하를 야기할 수 있습니다.
  • 최신 C# 환경에서는 제너릭을 활용한 타입 안정성이 잘 보장되므로, 박싱을 최소화하는 구조를 설계하세요.

핵심 포인트

  1. 박싱: 값 타입 → 참조 타입 변환 (힙 할당 발생)
  2. 언박싱: 박싱된 객체 → 원래의 값 타입 추출
  3. 성능 고려: 빈번한 박싱/언박싱 → GC 부담 증가, 런타임 캐스팅 검증
  4. 해결책:
    • 제너릭 컬렉션(List, Dictionary<TKey,TValue>) 사용
    • interface, object 기반 호출 최소화
    • 필요하다면 맞춤형 오버로드 또는 ToString()으로 사전 변환

코드 최적화와 유지보수성 사이에서 적절한 균형을 유지하되, 박싱/언박싱이 잦은 부분은 프로파일링을 통해 실제 성능에 미치는 영향을 확인하고 최적화하는 것이 좋습니다.


댓글

이 블로그의 인기 게시물

실버테크(Silver-Tech)

고령화 시대의 새로운 혁신, 실버테크(Silver Tech) 1. 실버테크란 무엇인가? 현대 사회는 빠른 속도로 고령화되고 있으며, 이에 따라 노년층을 위한 기술과 서비스가 더욱 중요해지고 있습니다. **실버테크(Silver Tech)**는 노년층의 삶의 질을 향상시키기 위해 개발된 기술과 서비스를 의미합니다. 건강 관리, 안전, 생활 편의성, 사회적 연결 등을 지원하는 다양한 혁신적인 기술이 포함됩니다. 2. 실버테크의 주요 분야 1) 헬스케어 및 원격 의료 스마트워치나 피트니스 트래커를 활용한  건강 모니터링 온라인 진료를 통해 병원 방문 없이 상담이 가능한  원격 의료 서비스 건강 상태를 분석하고 관리해주는  AI 기반 건강 관리 시스템 2) 스마트홈 및 생활 보조 기술 음성 인식으로 조작이 가능한  스마트 가전 낙상 감지 및 응급 호출 기능이 포함된  스마트 센서 노년층을 위한  자동화된 조명, 난방 시스템 3) 커뮤니케이션 및 소셜 기술 사용하기 쉬운 UI를 적용한  실버폰 및 태블릿 가족 및 친구와 소통을 쉽게 해주는  화상 통화 및 메시징 앱 노인 대상의  디지털 리터러시 교육 프로그램 4) 여가 및 엔터테인먼트 노년층을 위한  VR(가상현실) 체험 프로그램 온라인으로 제공되는  문화, 교육 콘텐츠 손쉽게 즐길 수 있는  전용 게임 및 앱 5) 이동성 및 모빌리티 솔루션 편리한 이동을 돕는  전동 휠체어 및 스쿠터 실시간 위치 추적이 가능한  스마트 네비게이션 시스템 고령자를 위한  자율주행 차량 및 호출 서비스 3. 실버테크가 가져오는 변화 - 삶의 질 향상 기술의 발전으로 인해 노년층이 더욱 독립적으로 생활할 수 있도록 도와주며, 건강하고 편리한 삶을 지원합니다. - 사회적 고립 해소 화상 통화, 소셜 네트워크, 온라인 커뮤니티 등을 통해 사회적 관계를 유지할 수 있도록 돕습니다. - 경제적 기회 창출 실버테크 산...

로또 번호 생성기

키움증권 Open API를 활용하여 갭 매매 전략 구현- C#

  1. 갭 매매 전략이란? 갭 매매란 주가가 전일 종가 대비 큰 폭으로 상승(갭업)하거나 하락(갭다운)하여 개장할 때, 발생한 갭의 움직임을 활용하여 수익을 내는 전략입니다. 이번에 소개할 전략은 다음 조건에 해당하는 종목을 대상으로 합니다. 조건검색 : 전일 대비 오늘 시가가 5% 이상 갭업으로 시작한 종목 전략 실행 : 개장 후 30분 이내에 주가가 전일 종가 수준까지 회귀하면 매수 진입 후, 갭이 완전히 메워질 때 매도합니다. 손절매 : 진입 가격에서 갭 상승분의 50% 하락 시 손절 익절매 : 갭 상승분의 50% 이상 추가 상승 시 익절합니다. 2. 구현 환경 및 준비사항 키움증권 API+ 설치 (영웅문 API) Visual Studio Community Edition (C#) 키움증권 계좌 (모의투자 계좌 추천) 3. C# 코드 전체 구현 (주석 포함) 다음 코드에는 갭 매매 전략, 주문 실행, 체결 확인 및 정정 주문까지 포함되어 있습니다. using System; using System.Collections.Generic; using AxKHOpenAPILib; public class GapTrading { private AxKHOpenAPI axKHOpenAPI; private string accountNumber; // 매수 후 체결된 정보를 저장할 Dictionary (종목코드, (체결가격, 체결수량)) private Dictionary< string , ( int price, int qty)> positions = new Dictionary< string , ( int , int )>(); public GapTradingStrategy (AxKHOpenAPI api, string accountNo) { axKHOpenAPI = api; accountNumber = accountNo; ...

Log4Net vs. Serilog 비교

🔍 Log4Net vs. Serilog 간단 비교 기준Log4NetSerilog 성능 중간 (충분하지만, 최신 라이브러리보단 느림) 빠르고 효율적 설정 방식 XML 기반 (전통적) JSON 기반 (모던함) 구조적 로깅 미지원 (기본 텍스트 로그) 강력한 구조적 로깅 지원 ASP.NET Core 통합 가능하지만 설정이 좀 복잡 간단하고 직관적 Sink(대상) 다양성 적당한 편 매우 다양하고 확장성 높음 생태계 & 유지보수 전통적, 유지보수 상태는 다소 정체 활발한 개발과 업데이트 🎯 어떤 프레임워크가 좋을까? Serilog를 추천하는 이유: 최신 기술 : ASP.NET Core와 완벽히 통합, 구조적 로깅이 뛰어나 JSON 로그 처리, 분석에 최적화됨. 높은 생산성 : 설정과 유지보수가 쉬움. 코드 기반 및 JSON 설정으로 직관적이고 빠른 개발이 가능. 확장성 : 파일, 콘솔, DB, Elasticsearch, Seq 등 다양한 Sink를 제공해 향후 확장성이 좋음. 🚀 결론 요약 Log4Net을 추천할 때Serilog를 추천할 때 기존 시스템과 호환이 필수 최신 ASP.NET Core 프로젝트 XML 설정 선호 JSON, 코드 기반 설정 선호 구조적 로깅 필요 없음 구조적 로깅과 분석 필요

C#으로 아두이노와의 시리얼 통신

  1. 들어가며 최근 IoT와 임베디드 시스템 개발이 증가하면서 PC와 아두이노 간의 통신을 효율적으로 처리하는 방법이 중요해졌습니다. 그중에서도 가장 널리 쓰이는 방법 중 하나가 바로  시리얼(Serial) 통신 입니다. 이번 글에서는 C#을 이용해 아두이노와의 시리얼 통신을 완벽히 이해하고 구현하는 방법을 자세히 소개하겠습니다. 2. 시리얼 통신이란? 시리얼 통신은 데이터를 한 번에 한 비트씩 순차적으로 전송하는 방식입니다. USB 포트를 통해 아두이노와 PC 간 데이터를 주고받을 때 주로 사용됩니다. 3. 준비물 아두이노(UNO, MEGA 등) PC 및 Visual Studio USB 케이블 4. 아두이노에서의 시리얼 통신 설정 아두이노 IDE에서 다음과 같은 코드를 작성하여 아두이노 보드에 업로드합니다. void setup () { Serial . begin ( 9600 ); // Baud rate 설정 } void loop () { if ( Serial . available ()) { char c = Serial . read (); // PC에서 받은 데이터 읽기 Serial . print ( "Received: " ); Serial . println (c); // 읽은 데이터를 다시 PC로 전송 } } 5. C#에서의 시리얼 통신 설정 C#에서는 System.IO.Ports 네임스페이스의 SerialPort 클래스를 사용합니다. 예제 코드 using System; using System.IO.Ports; class Program { static SerialPort port; static void Main () { port = new SerialPort( "COM3" , 9600 ); // 포트번호와 Baud rate 설정 port.Open(); port.DataReceived +...