Go
Go에서 embed를 이용해서 파일을 가져오는 방법에 대해 알아보자
Go에서 파일을 가져오는 방법은 여러가지가 있다. os.ReadFile()을 사용해서 파일을 읽어오는 방법도 있고, embed 패키지를 사용해서 파일을 가져오는 방법도 있다. 용도가 조금 다르지만, embed 패키지를 사용하면 빌드할 때 파일을 포함시킬 수 있어서 배포할 때 유용하다. 간단히 예를 살펴보면 다음과 같은 txt 파일을 만들어보자 hello.txtHello, World! 이제 embed 패키지를 사용해서 이 파일을 가져와보자.
2025년 4월 16일
Golang에서 form data를 Body에 담아 보내는 방법
일을 하고 있는데, 외부 API를 사용해야 할 일이 생겼다. 일반적인 JSON이나, XML을 주고 받는 API와는 다르게 form data를 주고 받아야 했는데, 필자의 경우 JSP때 다루어보고 현업에선 거의 다루지 않았던 지라, form data 자체가 사용한지 굉장히 오랜 시간이 지났다보니 Go에선 어떻게 하는지 잘 몰랐다. 이번에 이것저것 찾아보면서 정리해보려고 한다. form data란? form data는 HTML form에서 사용되는 데이터 형식으로, 주로 웹 애플리케이션에서 클라이언트가 서버로 데이터를 전송할 때 사용된다. form data는 key-value 쌍으로 구성되어 있으며, 일반적으로 application/x-www-form-urlencoded 또는 multipart/form-data 형식으로 인코딩된다.
2025년 4월 14일
Hugo Blog에 Mermaid 그래프를 렌더링해보자
현재 내 기술 블로그는 Hugo 라는 템플릿 엔진을 사용하고 있다. Hugo는 정적 사이트 생성기로, Markdown 파일을 HTML로 변환해주는 역할을 한다. 우리가 흔히 사용하는 마크다운에는 코드 블럭이라는 것이 있다. 아래의 Go 코드도 예쁘게 문법을 강조해서 보여주고, func main() { fmt.Println("Hello, World!") } Java 코드도 마찬가지로 코드 블럭을 통해 문법을 강조해서 보여준다.
2025년 3월 6일
Hugo에서 AI를 이용해 모든 포스팅을 번역해보자 (feat. 내 블로그가 5개 국어를 지원하게 된 건에 대하여)
과업 최근에 AI가 발전하면서 업무로 Gemini를 활용하게 되었다. 비정형 데이터를 이렇게나 잘 다루는 것을 보며, 예전에 했던 눈물나는 작업들이 머릿속에 떠오르기도 했다. 각설하고 예전부터 숙제처럼 느껴왔던 것이 있다. 바로 블로그 번역이다. 일전에 번역을 하게 되면 블로그를 보는 사람이 훨씬 많이 생긴다는 얘기를 듣곤 했는데, 예전에는 Google Translate API를 이용해서 번역해볼까 생각도하다가, 게으름에 못이겨 그냥 놔뒀었다. AI번역 분야도 퀄리티가 굉장히 좋아지면서, 이번 기회에 모든 포스팅을 번역해보는 프로젝트를 시작해보기로 했다. 그래서 해당 포스팅은 내가 블로그를 번역하게 된 계기를 소개하고, 어떻게 진행되었는지, 어떤 기능이 있는지를 소개하려 한다.
2025년 3월 6일
FSM과 함께 상태를 좀 더 효율적으로 관리해보자 with Golang
상태가 너무 많아! 업계에서 많이 일하다보면 정말 사소한 것 하나하나에도 상태가 있다는 것을 느끼게 된다. 예를 들어, 사용자의 로그인 상태, 주문 상태, 결제 상태 등등 많은 상태가 존재한다. 이러한 상태들은 일반적으로 코드나 DB에 저장되어 관리되며 상태에 따라 다른 로직을 수행하기 위해 사용한다. 전제 REST에서 핸들링하는 데이터 자체도 역시 Representational State Transfer의 약어로 상태와 관련된 것이지만, 해당 글에서 다루는 좀 더 협의의 상태를 다루게 될 것이다.
2025년 2월 22일
PR을 열 때 Github Actions를 통해 Golang 테스트를 자동으로 실행하고, 리포트를 생성해보자
어디든, 어떤 언어든 테스트를 위해 빌드나 테스트를 자동화하는 것은 매우 중요하다 볼 수 있다. 물론 로컬에서 직접 테스트를 수행하고 올리도록 할 수 있지만, 사람이 작업할 때는 테스트를 실행하지 않고 올리는 경우도 종종 있기에 너무 로컬에 의지하기 보단, 자동화를 해두면, 오래오래 사용하기에도 좋고, 유지보수에도 비용이 적게 들어갈 수 있다. 해당 글에서는 Golang에서 Test를 위한 액션을 만들어보고, PR을 열 때 자동으로 테스트를 실행하고, 리포트를 생성하는 방법에 대해 알아본다. 시나리오는 아래와 같이 설계했다.
2025년 2월 17일
Golang에서 Playwright를 이용해 크롤링을 해보자
데이터를 수집하는 방법에는 여러가지가 있는데, 가장 흔한 방식이 크롤링(Crawling)이라는 기법을 이용하는 것이다. 크롤링이란 직역하면 기어다니다 라는 뜻이다. 웹 크롤링은 웹을 기어다니면서 정보를 수집하는 것이라고 생각하면 된다. 도구 파이썬에서 특히 많이 사용하는데, 이유로는 파이썬 언어의 간결함도 있지만, 크롤링에 특화된 라이브러리가 많기 때문이다. 많이 알려진 도구로는 BeautifulSoup, Selenium, Scrapy 등이 있다. 최근에는 Playwright라는 도구가 나와있는데, 일반적으로는 크롤링보단 테스트 자동화에 많이 사용되지만, 크롤링에도 사용할 수 있다. 단순히 파서 역할만 하는 BeautifulSoup와는 달리, Playwright는 실제 브라우저를 기반으로 브라우저 관련 액션들을 제어할 수 있어서, JavaScript를 실행하거나, SPA(Single Page Application)를 크롤링할 때에도 유용하게 사용할 수 있다.
2025년 2월 14일
Golang에서 캐시 없이 테스트하기
Golang에서 테스트 할 때에는 2회차부터는 테스트 속도가 매우 빨라진다. 1회차 ok github.com/my/package/test 126.752s 2회차 ok github.com/my/package/test (cached) 1회차엔 2분이 넘게 걸렸지만, 2회차엔 시간을 잴 세도 없이 빠르게 테스트가 끝나버렸다
2025년 2월 14일
Golang에서 log.Fatal() vs panic() 차이점에 대해 알아보기
“이런 경우 log.Fatal()보다 panic()을 사용하는게 더 좋은 것 같아요” 최근 log.Fatal()을 사용하다 위와 같은 피드백을 들었다. 음? log.Fatal()은 log를 좀 더 잘 찍어주는거 아닌가? 하고 생각했다 부끄러운 사실이지만, 최근이 되어서야 Golang에서 log.Fatal()과 panic()의 차이점을 명확하게 알게 되어 이번 기회에 정리해보려고 한다. log.Fatal()과 panic()의 차이점 log.Fatal()과 panic()은 둘 다 프로그램을 종료시키는 함수이다. 코드로 동작을 살펴보자 package main import ( "log" "log/slog" ) func RunWithFatal() { log.Fatal("This is a fatal error") } func main() { RunWithFatal() slog.Info("This is not executed") } 위 코드를 실행하면 다음과 같은 결과를 볼 수 있다.
2025년 2월 11일
Golang에서 Singleflight로 중복 요청에도 강한 서버 만들기
최근 회사가 폐업하게 되면서 잠시동안 프리랜서로 일하게 되었다. 다행히도 뛰어나신 분을 만나 Go나 웹에 대해 여러 스킬에 대해 배우게 되었는데, 그 중 하나가 singleflight 패키지를 사용한 개선이었다. Singleflight? 영어로 직역하면, “단일 비행"이라는 뜻이다. 사실 너무 어렵게 이해할 필요 없이, 하나의 실행을 보장한다고 보면 된다. 예시를 보면 더더욱 간단한데 아래 코드를 보자 package main import ( "log/slog" "time" ) var counter int func DoSomething() { time.Sleep(5 * time.Second) counter++ slog.Info("result", "counter", counter) } func main() { for i := 0; i < 10; i++ { go DoSomething() } time.Sleep(10 * time.Second) } 2025/02/10 20:49:22 INFO result counter=3 2025/02/10 20:49:22 INFO result counter=4 2025/02/10 20:49:22 INFO result counter=10 2025/02/10 20:49:22 INFO result counter=5 2025/02/10 20:49:22 INFO result counter=2 2025/02/10 20:49:22 INFO result counter=6 2025/02/10 20:49:22 INFO result counter=7 2025/02/10 20:49:22 INFO result counter=9 2025/02/10 20:49:22 INFO result counter=1 2025/02/10 20:49:22 INFO result counter=8 당연스러운 결과겠지만, 고루틴이기에 순서는 보장되지 않으나 카운터는 1부터 10에 도달하게 되었다.
2025년 2월 10일
Golang에서 Lombok을 느껴보자
Lombok? Lombok은 Java에서 getter, setter와 같은 반복적인 코드를 줄여주는 라이브러리이다. Go 언어는 특성 상 Getter, Setter, 생성자와 같은 작업이 매우 귀찮다. 자바와 같은 경우 이런 작업을 자동으로 해주는 라이브러리가 존재한다. 예를 들어 아래와 같은 구조체에 생성자를 만든다고 가정해보자 type User struct { Name string Age int } 아마 대부분 다음과 같이 생성자를 만들어야 할 것이다.
2023년 10월 24일
Type Alias와 Named Type의 차이에 대해 알아보기 (Golang)
둘 다 타입 정의 시 기존의 타입을 재정의하는 방법이며 정의 방식은 아래와 같이 정의한다. type MyStruct struct { Name string Age int } func(m MyStruct) Print(){ fmt.Println(m.Name, m.Age) } type MyStructNamed MyStruct // Named Type type MyStructAlias = MyStruct // Type Alias Type Alias type alias의 경우 기존에 타입에 별칭을 지정하는 방식이기 때문에 기존 타입과 동일한 메서드를 가지고 있다.
2023년 10월 13일
Golang 에서 csv로 내보낸 파일에서 한글 깨짐 해결
BOM Excel은 UTF-8 인코딩된 CSV 파일을 올바르게 읽기 위해 파일의 문서 앞에 BOM이 필요하다. BOM: 바이트 순서 표시(Byte Order Mark, BOM)는 유니코드 문자 U+FEFF byte order mark로, 매직 넘버로서 문서의 가장 앞에 추가하여 텍스트를 읽는 프로그램에 여러 정보를 전달할 수 있다 In Code Golang에서 csv를 사용하는 경우 다음과 같이 한글이 깨지는 문제를 방지할 수 있다. package main import ( "encoding/csv" "os" ) func main() { file, err := os.Create("test.csv") if err != nil { panic(err) } defer file.Close() // NOTE: UTF-8 BOM 추가 file.WriteString("\xEF\xBB\xBF") writer := csv.NewWriter(file) defer writer.Flush() data := [][]string{ {"이름", "나이", "주소"}, {"홍길동", "30", "서울"}, {"김영희", "25", "부산"}, } for _, value := range data { err := writer.Write(value) if err != nil { panic(err) } } } Reference 바이트 순서 표식 - 위키백과
2023년 9월 23일
Golang에서 Template 사용하기 (Go Template)
Go Template Golang에는 Go Template이라는 기능을 활용하여 자신이 지정한 포맷에 따라 여러가지 데이터를 바인딩하거나, 조작한 문자열로 내보내는 기능이 있다. text/template 패키지 텍스트 기반 템플릿을 처리하기 위한 두 개의 표준 라이브러리 패키지이며, 정해진 포맷이나, 특정 로직에 따라 텍스트를 생성해야 하는 경우 자주 사용한다. text/template라는 패키지를 사용하여 텍스트를 정해진 포맷대로 내보내는 예시는 아래와 같다. package main import ( "os" "log" "text/template" ) type Person struct { Name string Age int } func main() { t := template.New("hello") t, err = t.Parse("Hello {{.Name}}, you are {{.Age}} years old.\n") if err != nil { log.Error(err) } p := Person{Name: "GiraffeWithCode", Age: 27} t.Execute(os.Stdout, p) } 코드에서 보다 싶이 Person이라는 구조체에 각 필드 Name, Age와 같은 필드가 템플릿 문자열인 "Hello {{.Name}}, you are {{.Age}} years old.\n"의 각 요소로 바인딩 될 것임을 쉽게 짐작해 볼 수 있다.
2023년 9월 23일