May 16, 2024 - 읽기 쉬운 코드

읽기 쉬운 코드를 길벗 리뷰어에 당첨되어 읽기 쉬운 코드를 읽어봤습니다.

내용이 재미있어 총 3번 정도 읽었는데, 소설책을 읽는 것처럼 쓱쓱 읽혀졌습니다.

예제 샘플은 C# 으로 되어있지만,
C# 은 자바와 비슷한 문법과 코드 형태를 가지고 있어,
코드 읽는데는 큰 문제는 없었습니다.

간단한 느낌점

『읽기 쉬운 코드』라는 책 제목은 개발자라면 꼭 한번 읽어야할 듯한 제목을 가지고 있습니다.

자기의 코드 조차도 읽기 어려워 하는 사람도 만나봤고,
남의 코드는 절대 못읽는 사람도 개발을 하다보면 경험해봤습니다.
이 사람은 남의 코드를 자신의 코드를

그리고 나의 코드가 과연 다른 사람에게 읽기 쉬울까? 라는 고민은 개발을 좋아하는 코더라면 자주 생각하는 영역일겁니다.
(코더라는 말을 비하의 언어로 쓰지 않는다면 말이죠. )

이 책은 코드의 가독성과 유지 보수성을 높이기 위한 다양한 방법을 제시하고 있습니다.
이 책을 통해 저자는 코드의 품질을 높이는 것이 얼마나 중요한지,
그리고 이를 위해 개발자가 어떤 노력을 기울여야 하는지를 명확하게 전달합니다.

책은 소설책을 읽는 듯 본인의 경험담과,
개발자의 실수,
본인이 그래서 어떻게 그걸 해결하기 위해 노력했는지 순으로,
독자에게 친근하게 설명합니다.

서론

코드는 작성하는 것보다 읽는 경우가 더 많다라는 부분에서
사람의 뇌는 컴퓨터가 가진 한계와는 완전히 다른 인지적 한계를 가지고 있다고 설명합니다.

컴퓨터는 프로그래머가 참조하도록 만든 정보만 사용해서 결정을 내리지만,
우리의 뇌는 성급하게 결론을 내리는 경향이 있습니다.
눈에 보이는 것이 전부인 사람이, 그런 개발자가 소프트웨어가 원하는 대로 동작하게 만들려면 당연히 코드를 작성해야 합니다.
그러므로 코드는 우리 뇌에 맞게 구조화해야하며, 코드는 반드시 인간 친화적이어야 합니다.

예전에는 메모리를 위해 어떻게 코드를 짧게 작성해야하는지를 고민했다면,
이제는 컴퓨터 성능이 좋아졌으니, 얼만큼 가독성을 높게 작성해야할지를 고민해야할 시대라는 생각이 들었습니다.

이해하기 어려운 코드는 작업 속도를 느리게 만듭니다.
반면 코드를 이해하기 쉽게 만드는 데 투자한 시간은 나중에 10배 이상으로 보상받게 됩니다.

책은 기존 코드베이스에서 지속적 배포를 다시 적용하는 어려움을 강조합니다.
코드는 작지만, 그 안에서 발생할 수 있는 문제들은 무궁무진하고,
보통 사이드 이펙트라고 한 가지 문제를 해결하면 다른 곳의 2개의 문제가 생기거나,
모든 문제가 해결된 것은 아니기 때문에 지속적인 개선과 유지 보수가 필요합니다.

이는 개발 현장에서 발생하는 다양한 문제들을 해결하는 데 있어 지속적인 노력이 필요하다는 점을 상기시켜 줍니다.

『읽기 쉬운 코드』는 읽기 쉽고 유지 보수하기 쉬운 코드를 작성하는 것이 얼마나 중요한지를 강조합니다.
책은 프로그래밍 언어의 기본적인 구조와 속성을 다루며,
객체지향형 프로그래밍 언어의 강점을 최대화하는 방법을 소개합니다.
이 과정에서 개발자 간의 협업이 필수적이며, 컴파일, 테스트, 디버깅,
배포 과정에서 발생하는 여러 가지 오류들을 체크하고 문제를 해결하는 것이 중요하다는 다시한번 고민해봅니다.

장점

책은 코드의 가독성과 유지 보수성을 높이기 위해 다양한 팁과 방법을 제시합니다.
예를 들어, 코드를 작성할 때 좋은 작명 규칙을 따르고,
코드 리뷰를 통해 문제를 조기에 발견하며, 버전 관리 시스템을 효과적으로 사용하는 방법 등에 대해서도 간략하게나마 설명합니다.

저자는 코드가 단순히 실행되는 것을 넘어서, 사람이 쉽게 이해할 수 있도록 작성되어야 한다고 강조합니다.
이는 특히 팀으로 협업할 때 중요한데,
새로운 팀원이 기존의 코드를 빠르게 이해할 수 있어야 새로운 기능을 추가하거나 기존 코드를 수정하는 데 어려움이 없기 때문입니다.

책은 이론적인 설명뿐만 아니라, 실제 예제를 통해 개념을 적용하는 방법을 보여줍니다.
이 과정에서 코드의 가독성을 높이는 다양한 방법을 직접 경험할 수 있을거라 기대하였습니다.

책은 또한 유지 보수와 버그 관리를 어떻게 효과적으로 할 수 있는지에 대해서도 다룹니다.
예를 들어, 버그가 발생했을 때 이를 우선적으로 해결하고, 결함을 쌓아두지 말 것을 권장합니다.
이는 린 소프트웨어 개발의 품질 내재화 원칙과도 일맥상통합니다.

저자는 코드가 제대로 동작하지 않는 이유를 이해하고, 이를 해결하는 과정을 통해 새로운 통찰력을 얻을 수 있다고 설명합니다.

이 책은 프론트, 백엔드 가리지 말고 한번 읽어보면 생각을 많이 하게 되네요.
특히 팀으로 협업하며 개발을 진행하는 실무자들에게 큰 도움이 될 것입니다.
또한 예비 개발자들에게도 읽기 쉬운 코드의 중요성을 깨닫게 하고, 더 나은 개발자가 되는 데 큰 도움이 될 것입니다.

단점

너무 많은 내용을 담으려다보니, 내용이 요약되거나 함축적으로 처리되는 부분이 많습니다.
예를 들어 저는 린 소프트웨어에 대해서는 잘 모르지만,
린 소프트웨어와 일맥상통한 부분이 있습니다와 같이 비교대상으로 사용되었을 때,
린소프트웨어에 대한 추가공부가 필요했습니다. 그게 나쁘다는 것보다는,
코드리뷰, 깃 사용법 등 하나하나만으로도 책이 나올 이야기를 다 담으려다보니,
경력자에게는 읽기 쉬운 내용이나, 초급자에게는 추후 경력이 쌓이면 다시 한번 읽어보면 다른 느낌이 들 책으로 느껴졌습니다.

또한 콘웨이의 법칙을 엄청 좋아하는 저에게 콘웨이의 법칙을 설명할때,
시스템을 설계하는 조직은 필연적으로 해당 조직의 커뮤니케이션 구조를 복제한 설계 구조를 만들어낼 수 밖에 없다.
라는 번역이 되어있었는데, 조금 난해하게 설명한(번역된) 부분이 있다고 생각합니다.
아마 논문 그대로를 번역하기 위해서라고 생각하겠지만,
시스템을 설계하는 조직은 그 조직의 커뮤니케이션 구조를 반영한 설계를 하게 됩니다로 외우고 있는 저도 몇번을 읽어봐야 뜻이 이해가 되었습니다.

그럼에도 이 책이 좋다고 생각이 든 이유는,
왜 제대로 동작하지 않는지 이해하지 못한다면,
일단 그 이유를 이해하는 데 주력해야 해야하며,
‘우연에 맡기는 프로그래밍’이 진행되는 것을 막자는 저자의 의도가 너무 명확했습니다.
코드가 왜 동작하는지 이해하지 못하거나, 실제로는 제대로 동작하지 않는다는 것을 진짜로 이해하지 못하는 개발자들을 많이 봐와서,
처음부터 코드를 이해한다면 문제를 더욱 쉽게 해결하자는 강한 의지가 느껴졌습니다.

문제를 설명하는 것만으로도 새로운 통찰력을 얻을 수 있습니다.
동료가 없다면 고무 오리에게 문제를 설명해보는 것도 좋다는 글도 재미있게 읽었습니다. 한 프로그래머가 고무 오리를 사용했기 때문에 이 기법은 고무 오리 디버깅이란 이름으로 알려져 있으며, 저는 요즘은 제가 정리하거나 궁금한 부분을 ChatGPT에 물어보게 됩니다.

그래서 이 책이 좋다고 느껴졌습니다. 코드를 작성하는 접근 방식을 다시 한번 생각해보았고,
더 나은 코드를 작성하기 위해 노력해야겠습니다.

Apr 26, 2024 - 런타임 데드 코드 분석 도구 Scavenger

당신의 코드는 생각보다 많이 죽어있다.

라는 제목으로 Naver Deview 2023 발표가 있었습니다.

요약된 내용은 아래와 같습니다.

시스템이 나이를 먹어갈수록 API client들이 서비스를 중단하거나,
기획 스펙 아웃 등의 여러 사유로 인하여 더 이상 사용되지 않는 코드(이하 데드 코드)가 늘어갑니다.

내 API를 사용하던 client들이 사용 중단을 알려주면 좋겠지만,
대부분 사용을 하지 않게 되었다는 사실을 API 제공자에게 알려주지 않습니다.

이런 데드 코드를 주기적으로 확인하고,
청소해 주면 시스템을 쌩쌩하게 유지할 수 있겠지만,
혹시나 이 API를 호출하는 클라이언트가 남아 있을지 모른다는 공포감이 데드 코드를 방치하게 하고,
방치된 데드 코드는 암덩어리처럼 자라게 됩니다.

이 암덩어리는 컴파일과 부팅 속도를 지연시키며,
테스트를 느리게 하고,
리팩토링을 힘들게 하는 등, 두고두고 개발자를 괴롭힙니다.

Scavenger는 이런 문제를 해결하고자 만들어진 DevOps 플랫폼이고
이미 70여 개의 크고 작은 네이버 서비스에 적용되어 있습니다.
네이버가 오픈소스로 공개하고 있는 Pinpoint와 유사한 방식으로 시스템 부팅 시에 Java agent만 설정해 주면, 자동으로 어떤 메서드가 언제 마지막으로 호출이 되었고,
어떤 메서드가 더 이상 호출되지 않는 것인지 기록합니다.
그리고 이를 UI를 통해 쉽게 확인할 수 있습니다.

이 발표에서는 이번 DEVIEW를 통해 오픈소스로 공개하는 Scavenger라는 런타임 데드 코드 분석 도구를 소개하고,
네이버 페이 등의 신규 시스템으로 재개발하는 중에 Scavenger를 활용하여 어떻게 몇십만 라인의 데드 코드를 제거했는지 설명합니다.
또한 Scavenger을 구현하는 데 사용한 bytecode instrumentation 등의 기술적 백그라운드에 대해서도 살펴봅니다.

Scavenger를 사용하면 여러분의 Java 코드가 날씬해집니다!

위의 내용에 대한 동영상은 아래와 같습니다.

요약 내용 : https://news.hada.io/topic?id=8610

  • 네이버가 공개한 오픈소스. 네이버내 80여개 서비스에서 사용중
  • 데드코드 : 실행되지 않는 코드 / 실행되더라도 어플리케이션 동작에 영향을 미치지 않는 코드
  • 데드코드의 문제점
    • 시스템을 이해/유지보수 하기 어렵게 만듦
    • 성능/보안에 악영향을 줌
    • 컴파일/테스트 속도를 지연시켜 전체 개발 속도를 저하
  • Scavenger는
    • 디버깅 또는 로그를 추가하지 않고 메서드 호출이 확인 가능
    • 메서드 호출 기록을 수집하여 이를 시각화해 유저에게 보여줌
    • Java agent 방식으로 손쉽게 사용 가능
  • JVM 기반 언어(Java, Kotlin)만 지원
    • Python은 현재 베타이고, 그외 다양한 언어 지원 예정

pdf 문서 링크 : https://deview.kr/data/deview/session/attach/%5B225%5D%EB%9F%B0%ED%83%80%EC%9E%84+%EB%8D%B0%EB%93%9C%EC%BD%94%EB%93%9C+%EB%B6%84%EC%84%9D+Scavenger+-+%EB%8B%B9%EC%8B%A0%EC%9D%98+%EC%BD%94%EB%93%9C%EB%8A%94+%EC%83%9D%EA%B0%81%EB%B3%B4%EB%8B%A4+%EB%A7%8E%EC%9D%B4+%EC%A3%BD%EC%96%B4%EC%9E%88%EB%8B%A4..pdf
github opensource 위치 : https://github.com/naver/scavenger

관련 블로그 등을 검색해봤을 때,
package 또는 class 단위에서 어느 정도의 method가 호출되었는지 확인할 수 있으며,
controller 에 사용하지 않는 API 파악에 매우 도움이 될 것이라고 판단합니다.

Apr 21, 2024 - CloudRun 도입 전 Log 설정

대부분의 프로젝트에서 구글 클라우드의 로깅을 위한 Logback 통합 라이브러리가 build.gradle 에 설정되어있습니다.

implementation 'com.google.cloud:google-cloud-logging-logback:0.129.1-alpha'

이번에 CloudRun 도입시 ‘Failed to find a usable hardware address from the network interfaces; using random bytes:’ 오류가 발생했고, 문제가 생긴 이유는 Netty 로 인한 오류였습니다.

추가적으로 https://mvnrepository.com/ 사이트에서 확인 시, 종속성으로 인한 취약점 또한 가지고 있습니다. (https://mvnrepository.com/artifact/com.google.cloud/google-cloud-logging-logback) 그로인해, 새로운 logback 라이브러리를 도입하는 것이 좋을 듯 하며, 추천하는 라이브러리는 다음과 같습니다.

Logback

implementation 'ch.qos.logback:logback-classic:1.5.5'

https://mvnrepository.com/artifact/ch.qos.logback/logback-classic

log4j

implementation 'org.apache.logging.log4j:log4j-api:2.23.1'

https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api

둘 중에 하나를 선택하면 됩니다.
log4j 의 경우 2021년 경 보안 이슈가 있었는데, 지금은 버전이 올라가 적용을 하여도 문제 없습니다. https://namu.wiki/w/Log4j%20%EB%B3%B4%EC%95%88%20%EC%B7%A8%EC%95%BD%EC%A0%90%20%EC%82%AC%ED%83%9C?from=Log4j#s-2.1 https://www.igloo.co.kr/security-information/apache-log4j-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EB%8C%80%EC%9D%91%EB%B0%A9%EC%95%88/

현재 logback 라이브러가 종속성으로 인한 취약점이 있다고 maver repogitory사이트에서 확인되어, log4j 를 설정하는게 오히려 좋지 않을까 판단합니다.

logback 과 log4j 둘다 적용하여, 정상적으로 GCP의 로그탐색기에 수집되어 동작하는 것을 확인했습니다.

로그 관련 라이브러리를 다 제거하고, 순수한 스프링부트의 내장 라이브러리를 사용해도 무방하며(logback 과 동일한 구조로 실행됩니다.), 그래도 구현체를 주입시키는 게 좋다고 생각하여, logback 이나 log4j 로 변환하는 것에 대해 의견을 정해 진행하길 원합니다.

logback.xml

GCP 에 CloudRun 이 도입되게 된다면 (모든 데이터는 로그탐색기에서 보게 되므로),
기존에 file로 저장하거나, console로 저장하거나 자체 커스텀 하던 logback-xxx.xml 을 console-appender.xml 만 사용하도록 정의하는게 좋습니다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <include resource="config/logback/console-appender.xml" />
    <logger name="kr.co.xxx.api" level="DEBUG"/>
    <root level="INFO">
        <appender-ref ref="APTICONSOLE"/>
    </root>
</configuration>

대부분 프로젝트는 console-appender.xml 과 file-rolling.xml 가 구성되도록 작업되어있고,
file의 경우 APTIROLLING , console의 경우 APTICONSOLE로 호출하게 개발된 것으로 파악됩니다.

CloudRun 의 경우, 위의 xml 로 정의된 것처럼 console(APTICONSOLE) 로 설정하면 됩니다.

Google Cloud Logging은 Google Cloud Platform (GCP)의 로그 관리 및 분석 서비스로 표준 출력 (stdout)과 표준 오류 (stderr) 스트림을 자동으로 수집합니다.
별도의 구성없이 자동으로 활성화되기 때문에, 로그 데이터를 로그탐색기로 보내어, 사용자가 쉽게 로그를 조회하고 분석할 수 있게 합니다.
오히려 실수로 APTIROLLING 와 APTICONSOLE를 2개가 동작하게 설정하면, 자동으로 로그 데이터를 2배로 보내게 되어, 관련 트래픽 비용이 2배로 발생하게 될 수 있습니다.

참고 사항1

private static final Logger log = LoggerFactory.getLogger(클래스명.class);

으로 해도 되나, 편리하게

@Slf4j

클래스 상단에 @Slf4j 를 설정하여 처리하는 게 코드 간결성이 더 편하다고 생각합니다.
Slf4j는 로깅에 대한 추상화 계층을 제공하는 라이브러리로, 다양한 백엔드 로깅 프레임워크 (예: Log4j, Logback, Java Util Logging 등)와의 통합을 제공해줍니다.
@Slf4j는 컴파일 시점에 실제 로깅 프레임워크를 결정하므로, 코드의 유연성을 높여주기에, Slf4j를 사용하면 코드의 종속성을 최소화합니다.

참고 사항2

로그를 전부 로그탐색기에 보게 되면, logback.xml 을 지금 처럼 다양한 파일(logback-local.xml, logback-qa.xml)이 아닌,

    <springProfile name="dev, stage, master">
        <logger level="INFO" name="xxx" />
        <root level="INFO" >
            <appender-ref ref="xxx" />
        </root>
    </springProfile>

와 같은 하나의 파일에서 관리하는 것도 좋다고 생각합니다.