|
| 1 | +--- |
| 2 | +date: 2025-09-26 |
| 3 | +category: [개발 회고] |
| 4 | +published: true |
| 5 | +fixed: false |
| 6 | +--- |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +## 서론 |
| 11 | +나는 블로그 유목민으로, 정말 많은 블로그를 개발하고 옮겨다녔다. |
| 12 | +GitHub Pages 블로그를 만들게된 기념으로, |
| 13 | +나의 블로그 활동 역사를 한 번 소개해보도록 하겠다. |
| 14 | + |
| 15 | +## Blog Haneum |
| 16 | +2021년에 처음 HTML, CSS를 배우고 나서, [Blog Haneum](https://github.com/chebread/blog-haneum)이라는 정적 블로그를 만들었다. |
| 17 | +처음 웹 개발을 배우고 만든 블로그인지라, 모든 부분을 내가 개발했다. |
| 18 | +블로그 본문 부분의 CSS도 다 스타일링했다. |
| 19 | + |
| 20 | +> 하나하나 개발하면서 즉각적으로 시각적인 결과물이 보여서 정말 재미가 있었다. |
| 21 | +> 이 블로그 개발로 하여금 웹 개발에 본격적으로 흥미가 생겼던 것 같다. |
| 22 | +
|
| 23 | +이 블로그를 스타일링 하면서 기억나는 것은, |
| 24 | +그 당시 클럽 하우스가 유행하던 때라 |
| 25 | +클럽 하우스의 기술 블로그를 참고해서 |
| 26 | +메뉴 팝업을 꾸몄었다. |
| 27 | + |
| 28 | +그러나, 이 블로그의 가장 큰 단점이 있었는데, 포스팅을 하려면 html 파일을 직접 만들어야 한다는 것이었다. |
| 29 | +그래서 나는 html 템플릿 파일을 하나 만들고, `cp` 명령어를 사용하여 복붙해서 포스팅을 했었다. |
| 30 | +정말 귀찮았었다. |
| 31 | + |
| 32 | +## 벨로그 |
| 33 | +Blog Haneum 블로그를 통해 포스팅을 했지만 Read only만 되는지라, 독자들과 상호작용을 할 수 없었다. |
| 34 | +그때 벨로그라는 서비스에 대해서 알게 되었다. |
| 35 | +벨로그에는 댓글 기능이 자체적으로 탑제되어 있어서 독자들과 상호작용을 할 수 있었다. |
| 36 | +그래서 2022년에 벨로그를 개설하고 포스팅을 하기 시작했다. |
| 37 | + |
| 38 | +처음으로 내가 쓴 [힙하게 코딩하는 방법](https://velog.io/@haneum/%ED%9E%99%ED%95%98%EA%B2%8C-%EC%BD%94%EB%94%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95)이 게시한 지 |
| 39 | +일주일도 안되서 피드 상위에 랭크되고, 좋아요가 50개를 넘어가서 너무 재밌었던 기억이 있다. |
| 40 | + |
| 41 | +> 참고로, 지금은 좋아요가 무려 80개에 육박한다. |
| 42 | +
|
| 43 | +그때는 중학생이었어서, 비판적인 댓글에 대해서 매우 불쾌함을 느껴 반박하는 댓글도 남기기도 했다. |
| 44 | +그 당시, 나는 개발자 문화인 '비판'에 대해서 아예 몰랐던 것이다. |
| 45 | +그 이후, 나는 지속적으로 개발을 하면서 비판에 대해 생각이 많이 바뀌었다. |
| 46 | +개발자에게 '비판'이란 인격 모독이 아니라, 나를 성장케 하는 양분이라는 것을 점차 알게 되었다. |
| 47 | + |
| 48 | +## Haneum Blog |
| 49 | +벨로그에서 활동을 하게 되면서 |
| 50 | +다른 개발자분들의 벨로그를 많이 보게 되었다. |
| 51 | +벨로그를 많이 보면서 나는 많은 개발자분들이 |
| 52 | +개인 사이트를 가지고 계신다는 사실을 알게 되었다. |
| 53 | +그래서 나를 소개하는 웹 사이트의 필요성에 대해서 자각하게 되었다. |
| 54 | + |
| 55 | +그 당시 나는 React를 사용하여 SPA를 배우고 있었다. |
| 56 | +React 자체는 매우 쉬웠지만, SPA의 핵심 동작 방식은 잘 알지 못했다. |
| 57 | +그래서 나는 SPA의 핵심 동작도 익힐겸, ES6의 문법도 익힐겸해서 |
| 58 | +SPA를 직접 만들어보기로 했다. |
| 59 | +그래서 [haneum blog](https://github.com/chebread/haneum-blog)라는 개인 블로그를 만들게 되었다. |
| 60 | + |
| 61 | +> 이번에도 직접 정적 사이트 빌더를 만들었듯, 나는 핵심적인 것을 공부해서 직접 만드는 것을 참 좋아한다. |
| 62 | +> 어렸을 때 레고를 참 좋아했는데, 레고 설명서를 보고 일단 만든 후에 부셔버린 후, |
| 63 | +> 설명서를 통해 만들면서 익힌 핵심적인 아이디어를 통해 내가 창작하여 다시 만드는 것을 좋아했다. |
| 64 | +> 이런 나의 유년시절에 발현된 경향성이, 지금 이렇게 프로그래밍에서도 여실히 보여주는 듯하다. |
| 65 | +
|
| 66 | +> 나는 그리고 이론과 실전은 항상 같이 가야한다고 생각한다. |
| 67 | +> 이론만 공부한다면, 실전에서 어떻게 이론을 활용해야 하는지 감도 오지 않는다. |
| 68 | +> 그래서 다시 또 이론을 공부하게 되고, ... |
| 69 | +> 이렇게 쳇바퀴 처럼 이론만 공부하는 바보가 되어버린다. |
| 70 | +> 그리고 실전만 한다면, 또 이론은 알지 못한다. |
| 71 | +> 이론을 알기위해서 이론을 공부하게 되면 |
| 72 | +> 너무 어려운 탓에 이론 공부를 도외시하게 되기 십상이다. |
| 73 | +> 그래서 내가 추천하는 방법은 이론과 실전을 함께 가라는 것이다. |
| 74 | +> 여담이지만, 내가 이번에 만든 정적 사이트 빌더도 Go 언어의 슬라이스를 공부하고 |
| 75 | +> 슬라이스 개념을 익히고자 진행한 프로젝트이다. |
| 76 | +
|
| 77 | +이 프로젝트를 개발할 때는 SPA를 직접 구축하는 것이다 보니, CRA의 도움을 받지 못했음으로 |
| 78 | + |
| 79 | +```js |
| 80 | +import webpack from 'webpack'; |
| 81 | +import fs from 'fs-extra'; |
| 82 | +import paths from '../config/paths.js'; |
| 83 | +import webpackConfig from '../config/webpack.config.js'; |
| 84 | +import SpeedMeasurePlugin from 'speed-measure-webpack-plugin'; |
| 85 | +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; |
| 86 | +const config = webpackConfig('production'); // start는 원래 development 환경에서 진행하기 때문에 그냥 인자를 이것으로만 줘도 괜찮다. |
| 87 | +const configWithSmp = new SpeedMeasurePlugin().wrap(config); |
| 88 | +configWithSmp.plugins.push( |
| 89 | + new MiniCssExtractPlugin({ |
| 90 | + filename: '[name].[hash:8].css', |
| 91 | + chunkFilename: '[name].[hash:8].chunk.css', |
| 92 | + }) |
| 93 | +); |
| 94 | +const compiler = webpack(configWithSmp); |
| 95 | +fs.emptyDirSync(paths.appBuild); // build directory remove |
| 96 | +fs.copySync(paths.appPublic, paths.appBuild, { |
| 97 | + dereference: true, |
| 98 | + filter: file => file !== paths.appHtml, |
| 99 | +}); |
| 100 | +const build = () => { |
| 101 | + compiler.run(); |
| 102 | +}; |
| 103 | +build(); |
| 104 | + |
| 105 | +``` |
| 106 | + |
| 107 | +직접 이렇게 Webpack과 Babel을 구성했다. |
| 108 | + |
| 109 | +그리고 `build.js`, `start.js` 이렇게 파일을 만들어서 |
| 110 | + |
| 111 | +```json |
| 112 | +"build": "node scripts/build.js" |
| 113 | +"start": "node scripts/start.js" |
| 114 | +``` |
| 115 | + |
| 116 | +이렇게 실행하곤 했다. |
| 117 | + |
| 118 | +```js |
| 119 | +import routes from './routes.js'; |
| 120 | +import render from './render.js'; |
| 121 | + |
| 122 | +const router = path => { |
| 123 | + if (!(path === window.location.pathname)) { |
| 124 | + window.history.pushState(null, null, window.location.origin + path); |
| 125 | + } |
| 126 | + if (!routes[path]) { |
| 127 | + if (!(path === '/')) { |
| 128 | + render('/404'); |
| 129 | + } |
| 130 | + return; |
| 131 | + } |
| 132 | + render(path); |
| 133 | + window.onpopstate = () => { |
| 134 | + if (!routes[window.location.pathname]) { |
| 135 | + if (!(window.location.pathname === '/')) { |
| 136 | + render('/404'); |
| 137 | + } |
| 138 | + return; |
| 139 | + } |
| 140 | + render(window.location.pathname); |
| 141 | + }; |
| 142 | +}; |
| 143 | + |
| 144 | +export default router; |
| 145 | +``` |
| 146 | + |
| 147 | +그리고 이렇게 `window` 객체를 활용해서 라우터를 구현 해보기도 했다. |
| 148 | + |
| 149 | +SPA를 구현하는데 있어서, 라우터가 가장 구현하기 힘들었다. |
| 150 | +라우터가 SPA의 핵심이었음으로, 일단 라우터가 만들어져야 SPA 사이트를 만들 수 있었다. |
| 151 | +그래서 거의 몇 주 동안은 라우터만 개발하고 개선했던 거 같다. |
| 152 | +그 당시에는 JavaScript도 잘 익히지 못했던 터라, |
| 153 | +정말 구현하는데 힘들었다. |
| 154 | +SPA를 직접 개발하면서, |
| 155 | +SPA 라이브러리/프레임워크를 그래서 쓰는 것이구나를 몸소 알게 되었다. |
| 156 | + |
| 157 | +소스코드가 더 궁금하다면, https://github.com/chebread/haneum-blog를 참고해보기를 바란다. |
| 158 | + |
| 159 | +새로운 블로그를 만들기는 했지만, 기존에 운영중이던 Blog Haneum은 종료하지는 않았다. |
| 160 | +이 블로그는 thisishaneum 프로젝트를 시작하면서 종료하게 되었다. |
| 161 | + |
| 162 | +> Blog Haneum을 종료하고 빠르게 thisishaneum을 만들 수 있을 줄 알았지만, |
| 163 | +> 1년이 넘게 소요될 줄은 꿈에도 몰랐다. |
| 164 | +
|
| 165 | +내가 SPA의 핵심적인 것을 이해하고자 |
| 166 | +Haneum Blog을 개발했지만, |
| 167 | +솔직히 SPA가 어떻게 돌아가는지 정확하게 알지는 못했다. |
| 168 | +왜냐하면 프레임워크/라이브러리가 로직이 보이지 않게 처리하고 있고, |
| 169 | +그것을 추상적인 개념으로서 기능화하여 개발자에게 제공하기 때문이다. |
| 170 | +거의 핵심적인 기능은 실제 코드 베이스를 봐야만 알 수 있다. |
| 171 | +그러나 라이브러리/프레임워크들이 고도화됨에 따라 |
| 172 | +코드 베이스가 너무 복잡해지고 커지기 때문에 |
| 173 | +기능을 알기 위해 코드 베이스를 모두 보고 공부한다는 것은 |
| 174 | +물리적으로 불가능하다. |
| 175 | + |
| 176 | +> 나는 솔직히 아직도 SPA가 어떻게 돌아가는지 알지 못한다. |
| 177 | +> 왜냐하면 프레임워크/라이브러리가 로직이 보이지 않게 처리하고 있고, |
| 178 | +> 그것을 추상적인 개념으로서 기능화하여 개발자에게 제공하기 때문이다. |
| 179 | +> 그래서 나는 SPA를 사용해서 개발하는데 너무 애를 먹었다. |
| 180 | +> 나는 이런 추상적인 기술을 배우는 것은 의미가 없다고 판단되서, |
| 181 | +> 그래서 프론트엔드 개발자를 포기하고, |
| 182 | +> 시스템 프로그래밍을 공부하게 되었다. |
| 183 | +> 추상적인, 분절된 지식을 배우는 것보다 핵심적인 기술을 배우는 것이 몇 배는 쉬운 것 같다. |
| 184 | +
|
| 185 | +## thisishaneum |
| 186 | +Haneum Blog를 만들었지만, |
| 187 | +내가 완벽주의 성향이 있는지라, |
| 188 | +약간 부족하다고 생각되어 새로운 개인 사이트를 만들게 되었다. |
| 189 | +해당 사이트는 블로그의 성격보다는, 나를 표현하는 것에 더욱더 초점을 맞춰 개발하고자 했다. |
| 190 | + |
| 191 | +그 당시 나는 SPA 라이브러리인 React를 공부하고 있었음으로 |
| 192 | +React로 정적 사이트를 만들게 되었다. |
| 193 | +그러나, React로 정적 사이트를 만드는 것은 매우 어려움이 많았다. |
| 194 | +Horizontal scroll을 구현하기 위해 |
| 195 | +GSAP 라이브러리를 사용하는데, |
| 196 | +MPA 방식의 사이트에서는 간단하게 JavaScript CDN만 불러오면 되는데, |
| 197 | +React 에서는 너무 설정할 것이 많았고, 매우 복잡하게 설정해야 했다. |
| 198 | +그리고 React 라이브러리의 동작 방식이 너무 추상적인지라, |
| 199 | +도대체 어떻게 다른 라이브러리를 불러와서 활용하는지에 대한 로직도 내 머릿속으로 시각화되지 않았다. |
| 200 | +솔직히 아직도 시각화가 되지 않는다. |
| 201 | +도대체 SPA 라이브러리/프레임워크는 어떤 위계, 절차로서 동작하고 있는 것인가? |
| 202 | +프론트엔드 라이브러리/프레임워크 보다 시스템 프로그래밍이 더 이해하기 단순한 거 같다. |
| 203 | + |
| 204 | +> 다만, 단순함이 쉬움을 의미하지는 않는다. |
| 205 | +
|
| 206 | +그 당시 이러한 깊은 동작 방식들을 알려고 하니, |
| 207 | +추상적인 내용들 밖에 나오지 않았다. |
| 208 | +그래서 이해를 포기했었다. |
| 209 | +그리고 그 당시에는 개발은 생산과 흡사하다고 생각했기 때문에, |
| 210 | +그냥 "동작하면은 됬지"라는 생각을 했던지라, |
| 211 | +딱히 깊이 있는 이해를 하지 않아도 상관은 없겠다라는 생각을 했다. |
| 212 | + |
| 213 | +> 지금 생각해보면 참 안일한 생각이다. |
| 214 | +> 개발은 본질적으로 생산이 아니기 때문이다. |
| 215 | +
|
| 216 | +내가 지금은 GitHub에는 공개하지 않고 있지만, |
| 217 | +이 당시 거의 1년 넘게 thisishaneum을 수 십개 버전으로 만들었었다. |
| 218 | +완벽주의가 심했던 터라, 다 만들어 놓았는데도 조금 바꾸고 싶어서 |
| 219 | +또 새롭게 만들고 그랬었다. |
| 220 | + |
| 221 | +> <img src="/assets/thisishaneum-history.webp" width="300"> |
| 222 | +> 이게 그 기록들이다. |
| 223 | +
|
| 224 | +그렇게 삽집을 계속 하면서, 이대로는 웹 개발 공부에 진척이 없을 것 같아 |
| 225 | +thisishaneum을 React로 만드는 것을 아예 포기하게 되었다. |
| 226 | + |
| 227 | +> 그러나 참 웃긴 것은, 이때 삽질하던 것이 지금 나에게 매우 큰 도움이 되었다는 것이다. |
| 228 | +> 이는, 프론트엔드 프로그래밍이 일개 마크업이 아니라는 것에 대한 증명이다. |
| 229 | +> 프론트엔드 개발 또한 프로그래밍이다. |
| 230 | +> 프론트엔드는 프로그래밍이 아니라는 주장은, |
| 231 | +> 프론트엔드 개발을 제대로 안해본 개발자들이 하는 주장이다. |
| 232 | +
|
| 233 | +## thisishaneum.com |
| 234 | +그 후, 2023년 내가 Next.js를 배운 후에, |
| 235 | +Next.js가 정적 사이트를 만드는데 아주 쉽다고 해서 |
| 236 | +다시 정적 사이트를 만드는 것에 도전하게 되었다. |
| 237 | +나는 삽질을 최소화하기 위해 다른 개발자가 만든 프로젝트를 참고해서 개발을 진행하였다. |
| 238 | +블로그 프로그램 로직은 [maxleiter](https://github.com/MaxLeiter/maxleiter.com/)를 참고해서 개발했다. |
| 239 | +블로그 디자인은 [Ryan Dahl](https://tinyclouds.org/)를 참고해서 개발했다. |
| 240 | + |
| 241 | +다른 것을 참고해서 개발하니 매우 순조롭게 개발이 끝났고, |
| 242 | +cloudflare로 Domain을 연결하고, vercel에 배포하니 |
| 243 | +매우 그럴듯한 [thisishaneum.com](https://github.com/chebread/thisishaneum) 블로그가 완성되었다. |
| 244 | +내가 창조한 것은 아니지만, 그래도 Haneum Blog 이후 제대로 배포해본 거는 오랜만이라 뜻깊게 여겨졌다. |
| 245 | + |
| 246 | +이렇게 thisishaneum.com 블로그를 개발하고 |
| 247 | +열심히 블로그 활동을 이어가던 중, |
| 248 | +하나 눈에 띄는 것을 발견하게 되었다. |
| 249 | +바로 나의 thisishaneum.com 리포지토리에 folk가 1로 되어 있는 것이다. |
| 250 | + |
| 251 | +누가 folk를 했을 까 궁금하여 history를 보니 어떤 인도 개발자가 folk를 한 것을 알게되었다. |
| 252 | +그러나, 얼마 지나지 않아 인도 개발자의 GitHub를 들어가보니 충격을 받았다. |
| 253 | +바로 나의 블로그를 그냥 folk해서 그대로 자기 사이트처럼 배포를 해놓고 운영중이었던 것이다. |
| 254 | +나의 포스팅을 영어로 번역해서 그대로 포스팅하고 있었다. |
| 255 | +참으로 충격이었다. |
| 256 | +그 때는 사람들이 아무도 안 볼줄 알고 나의 개인적인 글도 가득했는데, |
| 257 | +그대로 그 인도 개발자가 무단으로 복붙해서 활용중이었던 것이다. |
| 258 | +그 때 나는 오픈소스를 잘 활용해야 겠다는 생각이 번쩍 들었다. |
| 259 | +내가 MIT License를 사용했던 지라, 그 개발자를 신고할 수도 없었으므로, |
| 260 | +개인적으로 이메일을 통해 지워잘라고 재차 부탁해서, GitHub에서 만큼은 없애게 되었다. |
| 261 | +그 이후로 나는 블로그를 GitHub를 통해 포스팅할 때는 매사 신중히 포스팅하려고 하고 있다. |
| 262 | + |
| 263 | +이 인도 개발자 사태 이후, |
| 264 | +사건도 사건이고 |
| 265 | +블로그 또한 내가 직접 만든 것은 아닌지라 |
| 266 | +thisishaneum.com 블로그에 대한 애정이 많이 떨어지게 되었다. |
| 267 | +그래서 GitHub에 블로그 소스코드는 올려놓는 대신, 배포는 중단하게 되었다. |
| 268 | + |
| 269 | +## chebread.github.io |
| 270 | +다시 블로그를 직접 내 손으로 개발해봐야 겠다고 생각되어, |
| 271 | +2024년 초에 블로그 디자인을 완성했다. |
| 272 | + |
| 273 | +> 참고로, 이때 만든 디자인이 지금 현재 이 블로그이다. |
| 274 | +
|
| 275 | +디자인을 완성한 후에, Next.js로 개발을 하려고 했었다. |
| 276 | +그렇게 계획만 하고 있다가 거의 1년이 지나버렸다. |
| 277 | +블로그 개발이 늦어진 여러 이유들이 있겠으나, |
| 278 | +고등학교 진학 이후 어려움으로 인해 번아웃이 있었다. |
| 279 | + |
| 280 | +그렇게 2025년이 되고, |
| 281 | +프론트엔드를 포기하고 시스템 프로그래밍을 공부하였고, |
| 282 | +Go 언어를 공부하면서 문득 이런 생각이 들게 되었다. |
| 283 | + |
| 284 | +"Go로 정적 블로그 빌더를 만들면 어떨까?" |
| 285 | + |
| 286 | +Go 언어를 배우고 있는지라, 이론을 체화하는데에도 매우 큰 도움이 될 것 같아 |
| 287 | +블로그 정적 빌더를 만들게 되었다. |
| 288 | +디자인은 이미 만들어 놓은 것이 있어 순조롭게 개발되었다. |
| 289 | +그리고 로직 부분은 시대가 시대인지라, 생산적인 측면이라고 판단되는 것은 |
| 290 | +LLM을 사용했고, 핵심적인 로직은 내가 직접 구현했다. |
| 291 | + |
| 292 | +내가 구현한 로직은 여러 로직이 있지만, |
| 293 | +중요한 것들만 소개해보자면, |
| 294 | +PostsData로 데이터 중앙화를 했고 |
| 295 | +GetFilePaths를 클로저 방식으로 구현한 것이다. |
| 296 | +PostsData 로직은 너무 길어서 첨부하기는 그러므로, GetFilePaths 함수만 첨부하겠다. |
| 297 | + |
| 298 | +```go |
| 299 | +func GetFilePaths(dirPath string) []string { |
| 300 | + var files []string |
| 301 | + var f func(string) []string // 클로저 함수 타입 선언 |
| 302 | + f = func(dirPath string) []string { |
| 303 | + entries, err := os.ReadDir(dirPath) |
| 304 | + if err != nil { |
| 305 | + fmt.Println(err) |
| 306 | + } |
| 307 | + for _, v := range entries { |
| 308 | + if v.IsDir() { |
| 309 | + subDirPath := dirPath + "/" + v.Name() |
| 310 | + f(subDirPath) |
| 311 | + } else { |
| 312 | + if filepath.Ext(v.Name()) == ".md" || filepath.Ext(v.Name()) == ".mdx" { |
| 313 | + files = append(files, dirPath+"/"+v.Name()) |
| 314 | + } |
| 315 | + } |
| 316 | + } |
| 317 | + return files |
| 318 | + } |
| 319 | + f(dirPath) |
| 320 | + return files |
| 321 | +} |
| 322 | +``` |
| 323 | + |
| 324 | +더 많은 로직이 궁금하다면, https://github.com/chebread/chebread.github.io를 참고하기 바란다. |
| 325 | + |
| 326 | +그렇게 재밌게 Go로 정적 블로그를 완성하게 되었고, |
| 327 | +그 결과물이 지금 당신이 보고 있는 이 [chebread.github.io](https://chebread.github.io/) 블로그이다. |
| 328 | + |
| 329 | +이 블로그의 핵심 기능을 소개해보자면, |
| 330 | +파일명 기반 블로그 제목 및 링크 생성이다. |
| 331 | +즉, 파일명이 곧 블로그 제목이다. |
| 332 | +파일명에는 한글이 들어가도 상관 없고, 특수문자가 들어가도 상관이 없다. |
| 333 | +자동으로 정적 빌더가 처리해서 링크를 생성해준다. |
| 334 | +굳이 불편하게 마크다운 프론트 메터에서 `title`로 설정하지 않아도 되며, |
| 335 | +`slug` 속성을 지정하지 않아도 된다. |
| 336 | +파일명으로 `title`과 `slug`를 함께 지정할 수 있다. |
| 337 | +이는 기존의 정적 빌더에서는 않던 매우 편리한 기능이라고 볼 수 있다. |
| 338 | + |
| 339 | +이 기능 말고도 다른 재밌는 기능이 많으니 https://github.com/chebread/chebread.github.io를 참고해보기를 바란다. |
| 340 | + |
| 341 | +## 결론 |
| 342 | +나의 짧은 블로그 유목민 역사가 |
| 343 | +개인 블로그를 운영하고 싶은 개발자분들께 |
| 344 | +조금이나마 도움이 되기를 바란다. |
0 commit comments