DevSecOps를 위한 한걸음: Sonarqube를 활용한 지속적인 코드 품질 및 보안 관리

DevSecOps를 위한 한걸음: Sonarqube를 활용한 지속적인 코드 품질 및 보안 관리

요약: 이 글은 카카오페이에서 DevSecOps를 위해 어떻게 Sonarqube를 활용하고 있는지 쉽고 재미있게 설명합니다. 개발, 보안, 운영을 통합하여 처음부터 끝까지 보안을 고려하는 개발 방법론 DevSecOps 그리고 코드 품질을 관리하고 보안 취약점을 식별해 기술 부채를 줄이는 정적 코드 분석 도구 Sonarqube에 관심 있었다면 읽어 보는 걸 추천합니다.

💡 리뷰어 한줄평

geuru.geon DevSecOps에서 Sonarqube가 어떤 역할을 하는지 쉽게 파악할 수 있는 글입니다! 평소 Sonarqube 도입에 대해 고민하고 계셨다면, 이번 기회에 안전한 코드로 서비스를 운영해 보시는 건 어떨까요? 😀

owen.kh DevSecOps 환경 구성을 위해 카카오페이는 어떤 방향성를 가지고 있는지 알기 좋은 글입니다. 관련해 비슷한 고민을 하는 많은 담당자 분들께 도움이 되었으면 좋겠습니다~

시작하며

안녕하세요, 카카오페이 SRE팀 RE파트 데이빗입니다.
오늘은 DevSecOps와 Sonarqube에 대해 깊이 있게 알아보려고 해요. 어렵게 들리시나요? 걱정 마세요. 차근차근 설명해 드릴게요.

DevSecOps 소개

DevSecOps가 뭔가요?

DevSecOps는 Development(개발), Security(보안), Operations(운영)을 하나로 뭉친 말인데요. 쉽게 말해 “우리 처음부터 끝까지 보안에 신경 쓰면서 개발하자!”라는 뜻이에요. 예전에는 개발을 다 하고 나서 “자, 이제 보안 점검 좀 해볼까?” 하는 식이었어요. 마치 집을 다 짓고 나서 “앗, 도둑이 들어올 수 있겠네. 자물쇠를 달아야겠다!”라고 하는 것과 비슷하죠.
DevSecOps는 집을 설계할 때부터 보안을 고려하는 거예요. “이 창문은 좀 위험해 보이니 특수 유리를 쓰자”, “현관문은 이중잠금장치로 하자” 이런 식으로요. 개발할 때도 코드 한 줄 쓸 때마다 “이 로직은 안전한가? 보안에 문제없나?” 생각하면서 만드는 거죠.

DevOps와의 차이

“그럼 DevOps랑 뭐가 다른데요?”라고 물으실 수 있어요. DevOps가 개발과 운영을 하나로 묶었다면, DevSecOps는 여기에 보안을 더한 거예요.
DevOps를 맛있는 케이크를 만드는 과정이라고 생각해 볼까요? 개발팀이 케이크를 구워서 운영팀에게 건네주는 거죠. DevSecOps는 여기에 “케이크에 유해 성분은 없는지, 알러지 유발 물질은 없는지” 확인하는 과정을 추가한 거예요. 더 안전하고 믿을 수 있는 케이크를 만드는 거죠!

DevOps

  • 개발과 운영의 협업 강화
  • 빠른 배포와 피드백 루프 강조
  • 자동화를 통한 효율성 증대

DevSecOps

  • DevOps의 모든 장점 포함
  • 보안을 핵심 요소로 강조
  • 개발 초기 단계부터 보안 고려
  • 보안 팀과의 긴밀한 협업

전통적인 개발 방식과의 차이

전통적인 개발은 마치 릴레이 경주 같았어요. 개발팀이 달리고, 그다음 보안팀이 달리고, 마지막으로 운영팀이 달리는 식이었죠.
DevSecOps는 달라요. 세 팀이 손을 잡고 같이 달리는 거예요. 시작부터 끝까지 함께 가는 거죠. 이러면 중간에 실수가 생겨도 빨리 발견할 수 있고, 서로 도와가며 더 빠르고 안전하게 목표에 도달할 수 있어요.

DevSecOps 구현을 위한 핵심 요소

DevSecOps를 실제로 구현하려면 어떻게 해야 할까요? 세 가지 핵심 요소가 있어요.

자동화 및 보안 통합

자동화와 보안 통합은 DevSecOps의 핵심 요소예요! 자동화는 수동으로 수행하던 많은 보안 관련 작업들을 자동화함으로써 효율성을 높이고 인적 오류를 줄일 수 있어요.
예를 들어 볼까요? 여러분이 매일 아침 100개 이상의 보안점검을 한다고 생각해 보세요. 끔찍하죠? 하지만 보안점검을 진행하고 리포트해 주는 서비스가 있다면 어떨까요? 버튼 하나만 누르면 카테고리와 점검 요약 정보들로 알려주니 훨씬 편하겠죠? 개발에서도 마찬가지예요. 코드를 검사하고, 테스트하고, 보안을 확인하는 일을 자동으로 할 수 있어요.

보안 통합은 개발 생명주기 전체과정에 통합하는 것인데요. 요구사항 분석 단계부터 보안을 고려하고 개발 단계에서 안전한 코딩 표준 적용, 배포 단계에서의 안전한 구성관리를 해요.
카카오페이에서는 배포 통제 파이프라인을 통해서 자동화 및 보안 통합을 해요. 개발 단계 sandbox에서는 수시로 commit이 올라오고 PR이 발행되는데요. 변경사항이 발생할 때마다 즉 PR이 발행될 때마다 자동화된 배포 프로세스를 통해 확인을 하고 추가적으로 변경사항의 코드들에 대해 정적 분석, 취약점 분석 결과를 확인할 수 있어요.

지속적인 모니터링

지속적인 모니터링은 마치 24시간 CCTV를 켜놓은 것과 같아요. 실시간 모니터링을 통해 잠재적인 위협을 조기에 발견하고 대응할 수 있도록 해줘요.

개발 문화

DevSecOps는 단순히 도구를 쓰는 게 아니라 일하는 방식을 바꾸는 거예요. 마치 새로운 생활 습관을 들이는 것과 같죠. “보안은 보안팀만의 일이야”라고 생각하던 걸 “보안은 우리 모두의 일이야”로 바꾸는 거예요. 개발자도, 운영자도, 모두가 보안에 관심을 갖고 책임을 지는 Self Security 문화를 만드는 거죠.

Sonarqube 소개

자, 이제 Sonarqube 얘기를 해볼까요?

Sonarqube란?

Sonarqube는 DevSecOps의 든든한 조력자예요.
지속적인 코드 품질관리를 위한 플랫폼인데요. 정적 코드 분석을 통해 버그, 코드 스멜, 보안 취약점 등을 자동으로 탐지하고 관리할 수 있게 해주는 고마운 툴이에요.

주요 기능

Sonarqube가 어떤 기능들을 제공하는지 볼게요.

  • 버그 찾기: 잠재적 버그를 찾아내요.
  • 코드 스멜 감지: 지금은 문제없지만 나중에 문제가 될 수 있는 코드를 코드 스멜이라 부르고 그것을 찾아내는 거예요.
  • 보안 취약점 체크: 해커들이 좋아할 만한 부분을 미리 찾아내요.
  • 코드 중복 체크: 불필요하게 반복되는 코드를 찾아내요.
  • 코드 복잡도 분석: 너무 복잡한 코드는 나중에 유지보수하기 힘들잖아요? 그런 부분을 찾아줘요.

Sonarqube는 이 모든 걸 자동으로 해주니까, 개발자들이 더 중요한 일에 집중할 수 있게 해 줘요. 멋지지 않나요?

DevSecOps에서 Sonarqube의 역할

이제 Sonarqube가 DevSecOps에서 어떤 역할을 하는지 알아볼까요? Sonarqube는 마치 개발팀의 든든한 친구 같은 존재예요.

코드 품질 관리

Sonarqube는 여러분의 코드를 꼼꼼히 살펴봐요. 마치 엄마가 여러분의 방을 검사하는 것처럼요. “이 부분은 좀 지저분해 보여, 저기 먼지가 쌓여있네” 등과 같이 알려주는 거죠.
코드에서 이렇게 말할 거예요. “여기 중복된 코드가 있어요, 이 함수가 너무 길어요, 이 변수 이름이 애매해요” 이런 조언을 들으면 코드를 더 깔끔하고 이해하기 쉽게 만들 수 있겠죠?

보안 취약점 식별

Sonarqube는 보안 전문가의 눈을 가지고 있어요. 해커들이 약점으로 찾아낼 만한 부분을 미리 찾아내는 거죠.
예를 들어, “여기 비밀번호를 그대로 저장하고 있네요. 위험해요!” 같은 식으로 알려줘요. 이렇게 미리 알려주면 해커들이 공격하기 전에 막을 수 있겠죠?

기술 부채 관리

‘기술 부채’라는 말, 들어보셨나요? 돈으로 치면 부채를 지는 것처럼, 나중에 고쳐야 할 문제를 남겨두는 걸 말해요.
Sonarqube는 이런 기술 부채를 관리해 줘요. “이 부분은 나중에 문제가 될 수 있어요, 여기는 개선의 여지가 있어요” 하고 알려주는 거죠. 마치 집안 곳곳의 수리가 필요한 부분을 메모해 두는 것과 같아요. 이렇게 해두면 나중에 큰 공사를 하지 않아도 되겠죠?

카카오페이에서의 Sonarqube

앞서 말씀드렸듯이 DevSecOps는 개발 초기부터 보안에 신경 쓰며 개발하는 것이라고 했어요.
카카오페이에서는 Sonarqube를 개발 단계인 sandbox에서 사용을 하고 있어요. 어떻게 활용하는지 Sonarqube Custom Rule과 DevSecOps 사례 위주로 설명드릴게요.

Sonarqube Custom Rule

Sonarqube의 또 다른 멋진 점은 바로 ‘맞춤형 규칙’을 만들 수 있다는 거예요. 카카오페이에서는 각 팀의 지향점, 프로젝트 성격에 맞는 ‘맞춤형 규칙’을 만들어 사용하고 있어요.

장애 예방 및 재발 방지

모든 회사, 모든 프로젝트가 다 같을 순 없겠죠? 각자의 특별한 규칙이 있을 거예요.
예를 들어, 여러분의 회사에서 “모든 함수는 반드시 주석을 달아야 해”라는 규칙이 있다고 해볼게요. 그런데 Sonarqube에는 이런 규칙이 없을 수도 있죠. 이럴 때 여러분이 직접 규칙을 만들 수 있어요! 카카오페이에서는 장애가 발생했던 건들 중에서 정적분석으로 확인할 수 있는 것들은 Custom Rule을 만들어 재발 방지를 하고 있어요.

Custom Rule 생성 방법

규칙을 만드는 건 생각보다 어렵지 않아요. 마치 레고 블록을 조립하는 것처럼 할 수 있죠. 가장 쉬운 방법은 RULE TEMPLATE을 이용하는 방법인데요. 주제에 맞는 기본적인 템플릿을 제공하는 Rule이에요. 여기에서 만들고자 하는 규칙의 컨셉과 비슷한 게 있다면 커스텀해서 사용하면 쉽게 Custom Rule을 만들 수 있어요!


하지만 찾고자 하는 RULE TEMPLATE이 없다면 지금 알려드릴 방법으로 Custom Rule을 만들어보세요!
카카오페이에서는 Java, Kotlin 사용률이 압도적으로 높아요. 두 언어에 대해 Custom Rule을 생성하는 방법을 알려드릴게요. 어떤 Custom Rule을 정의할 것인지 ‘Rule 관련 class’와 Sonarqube에 등록하기 위한 ‘플러그인 class’가 필요해요. (사실 ‘Rule class 목록’, ‘File check 목록’, ‘Rule 저장소 정의’도 필요하지만 Custom Rule의 정의 부분에 초점을 두어 설명을 하려고 해요.)
자세한 가이드는 sonar-java문서를 참고하면 좋아요.

1) Java

먼저 Java에는 Custom Rule(정의 부분)을 생성하는 두 가지 방법이 있는데요.
JavaFileScanner(org.sonar.plugins.java.api.JavaFileScanner)와 IssuableSubscriptionVisitor(org.sonar.plugins.java.api.IssuableSubscriptionVisitor)를 상속받아서 구현하는 거예요.
각 방식에는 약간의 차이가 있는데 간략히 소개해 드릴게요.

JavaFileScanner

  • 오래된 방식이며, BaseTreeVisitor를 확장해요.
  • 전체 구문 트리를 순회하며 모든 노드를 방문해요.
  • 개발자가 관심있는 노드 유형에 대한 Visit 메서드를 오버라이드 해야해요.
  • 더 세밀한 제어가 가능하지만, 불필요한 노드도 모두 방문하여 성능이 떨어질 수 있어요.

IssuableSubscriptionVisitor

  • 새로운 방식이며, 성능이 개선되었어요.
  • 관심 있는 구문 트리 노드 유형만 선택적으로 방문 가능해졌어요.
  • nodeToVisit() 메서드를 구현하여 방문할 노드 유형을 지정해요.
  • 지정된 노드 유형에 대해서만 visitNode() 메서드가 호출돼요.
  • 필요한 노드만 방문하므로 일반적으로 더 효율적이에요.

코드로 비교를 해볼까요?

public class MyCustomRule extends BaseTreeVisitor implements JavaFileScanner{

    @Override
    public void scanFile(JavaFileScannerContext context){
        this.context = context;
        scan(context.getTree());
    }

    @Override
    public void visitMethod(MethodTree tree){
        super.visitMethod(tree);
    }
}
public class MyCustomRule extends IssuableSubscriptionVisitor {
	@Override
	public List<Tree.Kind> nodesToVisit(){
		return Collections.singletonList(Tree.Kind.METHOD);
	}

	@Override
	public void visitNode(Tree tree){
		MethodTree method = (MethodTree)tree;
	}
}

큰 틀은 비슷해 보이지만 IssuableSubscriptionVisitor를 사용할 경우 원하는 노드(Tree.kind.METHOD)만 방문해서 효율적으로 분석이 가능해요.
그럼 IssuableSubscriptionVisitor를 이용하여 위에서 언급했던 “모든 함수에 주석을 달아야 한다”를 Custom Rule로 만들어볼게요. Custom Rule을 생성하기 위해서는 앞서 설명드린 대로 ‘정의된 Rule’과 ‘플러그인’이 있어야 해요.

먼저, Rule을 정의해 볼게요.

package com.example;

import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.Tree;
import java.util.Collections;
import java.util.List;

@Rule(key = "MethodMustHaveComment", name = "Method Must Have Comment", description = "모든 함수는 주석을 달아야 해요")
public class MyCustomRule extends IssuableSubscriptionVisitor{
	@Override
	public List<Tree.Kind> nodesToVisit(){
		return Collections.singletonList(Tree.Kind.METHOD);
	}

	@Override
	public void visitNode(Tree tree){
		MethodTree method = (MethodTree)tree;
		Symbol.MethodSymbol methodSymbol = method.symbol();

		if(methodSymbol.isOverride()){
			return;
		}
		if(method.firstToken().trivias().isEmpty()){
			reportIssue(method.simpleName(), "This method doesn't have a comment");
		}
	}
}
  • @Rule 어노테이션을 사용하여 규칙의 키, 이름, 설명을 정의해요.
  • nodeToVisit() 메서드를 오버라이드하여 방문할 노드 유형을 지정해요. 여기서는 메서드 선언만 방문하도록 Tree.Kind.METHOD를 리턴해요.
  • visitNode(Tree tree) 메서드를 오버라이드하여 각 메서드 노드를 검사해요.
  • 오버라이드 함수는 제외해요. 그 이유는 상위 클래스에 이미 주석이 있을 것으로 가정하기 때문이에요.
  • 메서드의 첫 번째 토큰 앞에 주석이 없으면 이슈 리포트를 해요.

규칙을 만들었으니 이제 이 규칙을 사용할 수 있게 플러그인 클래스에 등록을 해볼게요.

package com.example;

import org.sonar.api.Plugin;

public class MyRulesPlugin implements Plugin{
	@Override
	public void define(Context context){
		context.addExtension(MyCustomRule.class);
	}
}

짜잔~

2) Kotlin

이번에는 Kotlin으로 Custom Rule 생성하는 방법을 볼 거예요.
Kotlin에서는 sonar-plugin-api에 추가적으로 sonar-kotlin-plugin도 추가해주셔야 해요.
PsiComment는 Intellij의 PSI 시스템의 일부로, Kotlin 컴파일러와 함께 번들로 제공됩니다.

import org.jetbrains.kotlin.com.intellij.psi.PsiComment
import org.jetbrains.psi.KtNamedFunction
import org.sonar.check.Rule
import org.sonar.kotlin.api.KotlinCheck
import org.sonar.kotlin.plugin.KotlinFileContext

@Rule(key = "MethodMustHaveComment", name = "Method Must Have Comment", description = "모든 함수는 주석을 달아야 해요")
class FunctionMustHaveCommentRule : KotlinCheck() {
	override fun visitNamedFunction(function: KtNamedFunction, context: KotlinFileContext) {
		if(!function.hasModifier(org.jetbrains.kotlin.lexar.KtTokens.OVERRIDE_KEYWORD)) {
			val functionBody = function.bodyBlockExpression
			val comment = function.node.psi.prevSibling
			if(comment !is PsiComment){
				context.reportIssue(function.nameIdentifier!!, "This function doesn't have a comment")
			}
		}
	}
}

동일하게 플러그인 클래스도 생성해요.

import org.sonar.api.Plugin;

class MyKotlinRulesPlugin : Plugin{
	override fun define(context: Plugin.context){
		context.addExtension(FunctionMustHaveCommentRule::class.java)
	}
}

모든 함수에 주석을 달기란 쉽지 않죠..

Sonarqube를 활용한 DevSecOps 사례

자, 이제 Sonarqube를 실제로 어떻게 사용하는지 몇 가지 예를 들어볼게요.

코드 리뷰 프로세스 개선

코드 리뷰, 해보셨나요? 동료의 코드를 검토하는 과정이에요. 그런데 이게 꽤 시간이 많이 걸리고, 주관적일 수 있어요.
Sonarqube를 사용하면 이 과정이 훨씬 쉬워져요. Sonarqube가 먼저 코드를 검사하고 문제점을 알려줘요. 보안적으로 부족한 부분도 분석을 해주기 때문에 리뷰어는 그 부분을 중점적으로 볼 수 있어요. 시간도 절약되고, 더 객관적인 리뷰가 가능해지는 거죠.

PR이 발행되면 Sonarqube에서 PR 분석을 진행하고, 분석한 결과를 github에 리포트해주고 있어요.
새로운 코드에서 Grade C를 맞아서 Quality Gate 통과에 실패했네요. 😭

16개의 Code smell이 발생했군요!

빠르게 코드 변경사항과 Sonarqube의 분석결과를 확인하고 코드리뷰를 진행해요.
PR을 발행했던 담당자는 Sonarqube 리포트와 피드백을 확인한 후 빠르게 수정 커밋을 해요.

Grade A를 받고 통과했네요 :)

보안 취약점 조기 발견

보안 문제는 빨리 발견할수록 좋죠. Sonarqube를 개발 과정에서 통합하면, 코드를 작성하는 즉시 보안 문제를 발견할 수 있어요.
예를 들어, 개발자가 코드를 작성하고 저장소에 올리면 Sonarqube가 바로 검사를 시작해요. “코드에 암호 또는 Access Key가 있어요!”라고 알려주면, 개발자가 바로 수정할 수 있겠죠? 이렇게 하면 보안 문제가 제품에 들어가기 전에 해결할 수 있어요.

장애 예방

Sonarqube는 잠재적인 버그나 성능 문제도 찾아낼 수 있어요. 이는 곧 장애 예방으로 이어지죠.
예를 들어, “이 부분은 이전에 장애가 발생했던 이슈로 변수 타입에 신경 써주세요, 오버플로우가 발생하기 쉬워요” 같은 경고를 해줘요. 이런 문제들을 미리 해결하면, 나중에 큰 장애가 발생하는 걸 막을 수 있겠죠?

마치며

여기까지 DevSecOps와 Sonarqube에 대해 알아봤어요. 어떠세요? 생각보다 어렵지 않죠?
DevSecOps는 개발, 보안, 운영을 하나로 뭉치는 멋진 방법이에요. 그리고 Sonarqube는 이를 실현하는데 큰 도움을 주는 도구죠. Sonarqube를 사용하면 코드 품질도 높아지고, 보안도 강화되고, 장애도 줄일 수 있어요. 게다가 개발자들의 실력도 자연스럽게 늘어나죠. 멋지지 않나요?
물론 처음에는 적응하기 어려울 수 있어요. “아, 참 깐깐하네”라고 생각할 수도 있죠. 하지만 시간이 지나면 Sonarqube가 없으면 불안해질 거예요. 마치 맞춤법 검사기 없이 글을 쓰기 힘든 것처럼요.
DevSecOps와 Sonarqube, 한번 시도해 보는 게 어떨까요? 여러분의 코드가 한 단계 업그레이드 되는 경험을 해보세요.
그리고 카카오페이는 이러한 기능을 가진 Sonarqube뿐만 아니라, 보안 강화를 위해 Fortify, Sonatype을 적용 검토를 하고 있어요. 강화된 보안만큼 안전하고 언제든 믿고 사용할 수 있는 서비스를 기대해 주세요.

참고 문헌

david.guetta
david.guetta

안녕하세요 카카오페이 SRE팀 RE파트 데이빗입니다. 제너럴리스트보다 스페셜리스트가 되고 싶은 한 우물만 파는 엔지니어입니다.