본문 바로가기
728x90
반응형
C# RichTextBox 줄간격(Line Spacing) 줄이기 - EM_SETPARAFORMAT 적용 방법 WinForms의 RichTextBox는 기본적으로 줄간격(Line Spacing)을 조절하는 속성을 제공하지 않는다. 폰트를 변경해도 줄과 줄 사이 간격은 그대로 유지되기 때문에, 줄간격을 변경하려면 Win32 API의 EM_SETPARAFORMAT 메시지를 직접 사용해야 한다.이번 작업에서는 RichTextBox의 줄간격을 줄이기 위해 여러 방법을 시도했고, 최종적으로 원하는 결과를 얻을 수 있었다. 적용 과정에서 겪었던 문제와 해결 방법을 정리해본다.문제 상황사용자가 텍스트를 입력하는 RichTextBox에서 줄간격을 조금 더 좁혀 한 화면에 더 많은 내용을 표시하고 싶었다.처음에는 아래와 같이 PARAFORMAT2와 EM_SETPARAFORMAT을 이용해 줄간격을 조정했다.private void .. 2026. 8. 25.
Visual Studio 2019 Attach to Process 시 "문서의 기호가 로드되지 않았습니다." 해결 방법 운영 중인 프로그램을 디버깅하기 위해 Visual Studio의 '프로세스에 연결(Attach to Process)' 기능을 사용했습니다.하지만 브레이크포인트에 다음과 같은 메시지가 표시되며 디버깅이 되지 않았습니다.문서의 기호가 로드되지 않았습니다.브레이크포인트가 빈 원으로 표시되고, 해당 위치에서 중단되지 않는 현상이 발생했습니다.원인이 오류는 대부분 실행 중인 EXE 또는 DLL과 Visual Studio에서 가지고 있는 PDB(Symbol) 파일이 서로 일치하지 않을 때 발생합니다.예를 들어,Visual Studio에서는 방금 Debug 빌드한 DLL을 기준으로 디버깅하려고 하는데실제 실행 중인 프로그램은 이전 버전의 DLL을 사용하고 있는 경우Visual Studio는 현재 실행 중인 코드와 소.. 2026. 7. 10.
C# DevExpress XtraReport를 PDF(Base64)로 변환하여 API로 전달하기 외부 API 연동 시 다음과 같이 PDF 파일을 Base64 문자열로 변환하여 JSON에 포함해야 하는 경우가 있다.[ { "docType": "pdf", "documentId": "12345", "issueDate": "20250101", "file": "PDF파일(Base64 인코딩)" }] 이번 글에서는XtraReport → PDF 변환 → Base64 인코딩 → JSON 생성과정을 정리한다.1. 전체 처리 흐름 XtraReport ↓ ExportToPdfMemoryStream ↓ ToArray()byte[] ↓ Convert.ToBase64String()Base64 문자열 ↓ JSON에 삽입API 전송 2. XtraReport를 PDF로 변환하기DevExp.. 2026. 2. 26.
이력서에는 안 쓰이지만, 실무에서는 중요한 판단들 — 티 나지 않게 시스템을 살린 선택들들어가며이력서를 쓰다 보면자연스럽게 이런 항목들만 남는다.성능 개선 30%쿼리 튜닝공통 모듈 설계테스트 도입하지만 실무에서 하루하루 시스템을 굴리며 내린 결정들 중에는이력서에 쓰기 애매한 판단들이 훨씬 많다.안 고친 코드안 만든 구조안 도입한 기술일부러 남긴 복잡함이 글은그런 판단들이 왜 중요했고,왜 이력서에는 잘 안 남는지를 정리한 기록이다.1️⃣ “이건 고치지 않는 게 낫겠다”라는 판단코드가 깔끔해 보이지는 않지만오랫동안 안정적으로 돌아가고 있고변경 리스크가 크며얻는 이득이 명확하지 않을 때이런 경우,고치지 않는 선택이 가장 기술적인 결정이 된다.이 판단은성과로 남기기 어렵지만,장애를 막는다.2️⃣ “공통화하지 않는 게 맞겠다”라는 판단지금은 비슷해 보여도변경 단.. 2026. 1. 30.
성능보다 결과 안정성을 우선한 선택 — 빠르게 만드는 대신, 흔들리지 않게 만들었다들어가며쿼리 성능을 이야기할 때대부분의 목표는 하나다.“더 빠르게.”하지만 실무에서 실제로 문제를 일으키는 건속도가 아니라 결과의 흔들림인 경우가 훨씬 많다.이 글은쿼리를 더 빠르게 만들 수 있었음에도,의도적으로 성능보다 결과 안정성을 우선했던 선택에 대한 기록이다.상황 요약문제가 되었던 쿼리는 다음과 같은 특징을 가지고 있었다.현재 성능: 충분히 빠름실행 계획: 안정적결과: 항상 정확사용 위치: 핵심 업무 화면즉,“고치지 않아도 되는 쿼리”처럼 보였다.그런데 성능 개선을 위한 선택지들이여러 개 떠오르기 시작했다.고려했던 성능 개선 방법들서브쿼리 → JOIN 변경인덱스 힌트 사용캐시 테이블 도입조회 조건 일부 제거이 방법들은테스트 환경에서는 실제로 속도를 개.. 2026. 1. 30.
이 쿼리는 빨라도 위험하다고 판단한 이유 — 지금 빠른 쿼리가 항상 안전한 건 아니다들어가며쿼리 튜닝 이야기를 하면대부분 이렇게 시작한다.“지금 실행 시간은 얼마인가요?”그리고 이렇게 끝난다.“충분히 빠르네요. 문제 없어요.”하지만 실무에서 문제를 일으킨 쿼리들을 돌아보면,많은 경우 처음부터 느렸던 쿼리가 아니었다.이 글은현재 기준으로는 빠르지만,구조적으로 위험하다고 판단해 미리 손본 쿼리에 대한 기록이다.문제의 쿼리: 지금은 아무 문제 없어 보였다해당 쿼리는 이런 상태였다.실행 시간: 수십 ms실행 계획: 정상인덱스 사용: OK사용자 체감: 없음즉,“왜 이걸 고쳐요?”라는 질문이 먼저 나오는 상황이었다.그런데 불편한 신호들이 보이기 시작했다1️⃣ 속도가 아니라 “의존 조건”이 불안했다쿼리는 특정 전제에 강하게 의존하고 있었다.특정 날짜 범위특.. 2026. 1. 30.
한 줄을 허용해도 되는 기준 — 짧아도 괜찮은 코드에는 공통점이 있다들어가며앞선 글에서한 줄 코드가 설계를 망치는 순간을 이야기했다.하지만 모든 한 줄 코드가 문제는 아니다.실무에는 오히려 한 줄로 남겨야 더 좋은 코드도 분명히 존재한다.이 글은“한 줄을 쓰지 말자”가 아니라,“언제는 한 줄로 써도 되는가”를 명확히 하기 위한 기준 정리다.한 줄이 허용되는 코드의 본질한 줄이 허용되는 코드에는공통된 특징이 있다.그 한 줄이판단을 숨기지 않고,의도를 바로 말한다.기준 1️⃣ 하나의 개념만 담고 있을 때허용되는 예 isVisible = items.Count > 0;이 코드는컬렉션 존재 여부UI 표시 여부두 개념이 아니라 하나의 개념이다.“아이템이 있으면 보인다.”경계해야 할 예 isVisible = items.Count > 0 && u.. 2026. 1. 30.
한 줄 코드가 설계를 망치는 순간 — 짧다고 해서 단순한 건 아니다들어가며개발을 하다 보면이 말을 자주 듣는다.“이거 한 줄이면 되는데요?”실제로 한 줄로 해결되는 문제도 많다.그래서 더 위험하다.이 글은문법적으로는 한 줄이지만,설계적으로는 너무 많은 책임을 떠안은 코드가어떻게 시스템을 망치기 시작하는지에 대한 기록이다.한 줄 코드가 주는 착각짧은 코드는이런 인상을 준다.간단하다이해하기 쉽다고칠 게 없어 보인다예를 들어 이런 코드다. control.Enabled = user.CanEdit && data.IsValid && !isLocked;딱 보면“무슨 문제 있나?” 싶다.그런데 이 한 줄에는 무엇이 들어 있나조금만 뜯어보면이 한 줄에는 다음이 모두 들어 있다.사용자 권한 판단데이터 상태 판단화면 잠금 상태 판단UI 표현 결정즉,서로 다.. 2026. 1. 30.
언제는 테스트를 다시 도입해야 하는가 들어가며앞선 글에서나는 특정 UI 화면에 대해자동 테스트를 작성하지 않기로 결정한 이유를 정리했다.하지만 이 결정에는중요한 전제가 하나 있다.이 선택은 영원하지 않다.이 글은그 판단을 언제, 어떤 신호에서 다시 뒤집을 것인지를미리 정리해 둔 기록이다.왜 “되돌릴 조건”이 필요한가테스트를 안 하기로 한 결정이가장 위험해지는 순간은 이거다.“어차피 이 화면은 테스트 안 하기로 했잖아요.”그 순간,판단은 기준이 아니라 관성이 된다.그래서 나는테스트를 포기할 때 동시에다시 도입해야 할 조건도 같이 정리해두는 편이다.신호 1️⃣ 요구사항 변경이 “구조적”으로 바뀌기 시작할 때이런 변화가 보이면 위험 신호다조건 하나 추가가 아니라 흐름 자체가 바뀜UI 규칙이 늘어나기 시작함기존 상태 계산이 자주 수정됨이 시점부터는.. 2026. 1. 30.
이 화면은 테스트를 포기한 이유 — 테스트를 안 한 게 아니라, 다른 선택을 한 것이다들어가며UI 개발 이야기를 하다 보면이 질문을 피할 수 없다.“이 화면 테스트는 어떻게 하고 있나요?”이상적인 답은 분명하다.“자동 테스트로 다 검증하고 있습니다.”하지만 실무에서는모든 화면이 그 답을 가질 수는 없다.이 글은테스트의 중요성을 부정하지 않으면서도,특정 UI 화면에 대해서는의도적으로 테스트를 작성하지 않기로 결정했던 이유를정리한 기록이다.테스트를 고려했던 화면의 특징문제가 된 화면은 다음과 같은 성격을 가지고 있었다.사용자 입력 흐름이 복잡함조건에 따라 UI 상태가 자주 바뀜포커스, 활성화, ReadOnly 같은 제어가 많음이벤트 타이밍에 민감함즉,로직보다 “흐름”이 중요한 화면이었다.처음에는 테스트를 하려고 했다당연히 테스트부터 떠올렸.. 2026. 1. 30.
읽기 쉬운 UI 코드와 고치기 쉬운 UI 코드의 차이 — 처음 보는 사람과, 나중에 수정하는 사람 사이들어가며UI 코드를 리뷰하다 보면이런 말이 자주 나온다.“이 코드, 읽기는 진짜 쉽네요.”그런데 막상 수정하려고 하면분위기가 바뀐다.“이거… 어디부터 건드려야 하지?”이 글은읽기 쉬운 코드와 고치기 쉬운 코드가왜 다른 방향으로 진화하는지,그리고 실무에서는 왜 후자를 더 우선하게 되었는지를정리한 기록이다.읽기 쉬운 UI 코드의 특징읽기 쉬운 코드는 보통 이런 장점을 가진다.코드 길이가 짧다흐름이 직선적이다한 눈에 무슨 일을 하는지 보인다이벤트 하나에 동작이 모여 있다 void TextChanged(object sender, EventArgs e){if (conditionA && conditionB)control.Enabled = false;}처음 보면이해는 .. 2026. 1. 30.
UI에서 상태를 하나 더 만들지 않은 이유 — Boolean 하나가 시스템을 복잡하게 만드는 순간들어가며UI 코드를 짜다 보면이런 순간이 자주 온다.“여기 상태 하나만 더 두면훨씬 간단해질 텐데?”isEditable, isLocked, isDisabledBoolean 하나 추가하면조건문도 줄고, 코드도 읽기 쉬워진다.그래서 더 위험하다.이 글은상태를 하나 더 만들면 오히려 복잡해질 거라고 판단해의도적으로 추가하지 않았던 경험을 정리한 기록이다.처음에는 상태를 추가하고 싶었다문제가 된 UI에는 이런 요구가 있었다.특정 조건에서만 수정 불가데이터 상태 + 사용자 권한 + 화면 모드에 따라 동작 달라짐조건이 코드 곳곳에 흩어져 있음이 상황에서 가장 쉬운 해결책은 이거였다. isReadOnly = true / false이 상태 하나만 있으면모든 조건을 .. 2026. 1. 30.
728x90
반응형