What is Hackers' Pub?

Hackers' Pub is a place for software engineers to share their knowledge and experience with each other. It's also an ActivityPub-enabled social network, so you can follow your favorite hackers in the fediverse and get their latest posts in your feed.

0
0
0

์‚ฌ์‹ค ๋กœ์˜คํžˆ๋„ ๋˜‘๊ฐ™์€ ์ด์œ ๋กœ ์ ‘์—ˆ๋Š”๋ฐ ๋ฆฌ๋“ฌ๊ฒŒ์ž„ ๊ฐ™์ด ๋งค์ผ ๋˜‘๊ฐ™์ด ํ•˜๋”๋ผ๋„ ์žฌ๋ฏธ์žˆ์„ ์ˆ˜๋Š” ์—†๋Š”๊ฒƒ์ผ๊นŒ

์‚ฌ์‹ค ํ”„๋กœ์„ธ์นด๋„ ๋งค์ผ ๋ถ€์ŠคํŠธ ๊ผฌ๋ฐ•๊ผฌ๋ฐ• ๋น„์›Œ์ฃผ๋Š”๊ฒŒ ์ˆ™์ œ๊ฐ™์€ ๊ฑด๋ฐ๋„ ์ˆ™์ œ๊ฐ™์ด ๋А๊ปด์ง€์ง€ ์•Š์œผ๋‹ˆ๊นŒ

RE:
https://sekai.social/notes/airiciiino6h02jx

0
0

Ve francouzskรฉm Lyonu podlehl zranฤ›nรญm tล™iadvacetiletรฝ muลพ, kterรฉho ve ฤtvrtek napadla skupina pachatelลฏ. K tomu doลกlo na okraj univerzitnรญ konference krajnฤ› levicovรฉ europoslankynฤ›. Pล™รญpad vyvolal prudkรฉ politickรฉ reakce. Prezident Macron vyzval ke klidu.
๐ŸŒ Vรญce pล™ibliลพuje zahraniฤnรญ zpravodaj ฤŒT ve Francii Jan ล mรญd.

0
0
0
0
0

I've been honestly adding Co-Authored-By: Claude <noreply@anthropic.com> to every commit where I used an LLM even slightlyโ€”whether it's generating test scaffolding, drafting docs, or just bouncing ideas. I thought transparency was the right thing to do. Turns out, people see that trailer and immediately assume the whole thing is โ€œvibe codedโ€ AI slop, no further questions asked. The irony is that being honest about my process is what's getting my work dismissed.

Now I'm genuinely torn. Do I keep the trailer and accept that some people will write off my work at a glance? Or do I drop it and lose something I actually believe in? It's frustrating that there's no widely understood distinction between โ€œI prompted an LLM to write my entire appโ€ and โ€œI used an LLM as a tool while writing my own code.โ€ I don't have an answer yetโ€”just sitting with the discomfort for now.

1

en retard...

J'ai terminรฉ de lire ", les chroniques du flash" et c'รฉtait un vrai plaisir ร  lire. Merci @ploum

Dans un monde post-apocalyptique trรจs proche de nous, sans technologie, sans trop d'รฉlectricitรฉ, oรน le vรฉlo prend une place importante pour celles et ceux qui veulent voir plus loin que leur village.

Je recommande.

bikepunk.fr

0

์ตœ๊ทผ ์‚ฌ์ดํด๋ง ์„ ์ˆ˜์˜ ํŠธ๋ ˆ์ด๋‹ ์ค‘ ๋‚œ ์‚ฌ๊ณ ๋Š” ์ •๋ง ์•ˆํƒ€๊น๋‹ค. ์ œ์ฃผ ๋บ‘๋บ‘์ด ๋Œ๋‹ค๋ณด๋ฉด ํ…… ๋นˆ ์‚ฐ๊ฐ„๋„๋กœ์—์„œ ์ € ํ›ˆ๋ จ ํ•˜๋Š” ๋ถ„๋“ค์„ ๋งˆ์ฃผ์น  ์ˆ˜ ์žˆ๋Š”๋ฐ ์‚ฌ์‹ค ๋ˆˆ ๊ฐ๊ณ  ๋‹ฌ๋ฆฌ๋Š” ๊ฑฐ๋ผ ์šด์ „์ž ๋ง๊ณ ๋„ ์ „๋ฐฉ ์ฃผ์‹œํ•˜๋Š” ์‚ฌ๋žŒ์ด ๋” ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ทธ ์‚ฌ๋žŒ์ด ์„ ์ˆ˜์—๊ฒŒ ๋ฐ”๋กœ ์˜์‚ฌ ์ „๋‹ฌ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค. ๊ทผ๋ฐ ...... ๋ณดํ†ต ๊ทธ๋ ‡๊ฒŒ ์ค€๋น„ํ•˜์ง„ ์•Š๋Š”๋‹ค. ๋น ๋ฅธ ์†๋„๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์›๋ž˜ ์ด ํ›ˆ๋ จ์ด ์ฐจ๋กœ ๋ฐ”๋žŒ ๋ง‰๊ณ  ๋ƒ…๋‹ค ๋‹ฌ๋ฆฌ๋Š”๊ฑฐ ๋งž๋‹ค. ๊ทผ๋ฐ ...... ๋„๋กœ ์ฃผ์‹œ๊ฐ€ ์•ˆ๋˜๋Š” ์ƒํ™ฉ์€ ...... ๋„ˆ๋ฌด๋‚˜ ์œ„ํ—˜ํ•œ๊ฑฐ ๋งž๋‹ค.

0

, รฉpisode 09.
Il รฉtait lร , seul, m'attendant malicieusement sur l'une des tables de la librairie dans laquelle j'ai mes habitudes. Comment aurais-je pu lui rรฉsister ? J'avais dรฉjร  succombรฉ aux รฉchos de "La montagne magique" deux ans plus tรดt.

L'histoire est connue, ne serait-ce que par la lรฉgende que l'adaptation de Visconti fait encore rรฉsonner ici et lร . C'est celle d'un homme vieillissant qui tombe sous le charme d'un jeune adolescent dans une Venise en proie ร  une dangereuse รฉpidรฉmie.
La magie des mots et des atmosphรจres, comme seul Thomas Mann en a le secret. Un instant suspendu.

Thomas Mann โ€“ La mort ร  Venise (1912)

Thomas Mann โ€“ La mort ร  Venise (1912)

, รฉpisode 10.
Difficile de rรฉsister ร  Bikepunk et ร  @ploum quand on est sur le Fรฉdiverse et que l'on creuse, explore la thรฉmatique solarpunk.
Bikepunk est-il solarpunk d'ailleurs ?

Bikepunk en tout cas est solide. C'est dรฉjร  beaucoup. Il se joue de quelques codes ici ou lร . Et c'est pas mal non plus. Il dessine un espoir dans le chaos. Ce qui est trรจs bien. Alors on le rangera dans une bibliothรจque de SF qui commence ร  compter de plus en plus d'imaginaires positifs : pas loin d'Eutopia, de Visite, de Becky Chambers et de quelques autres.

Mais quoi ? Ce n'est pas comme s'il restait des gens ร  convaincre par ici. Si ?

Ploum โ€“ Bikepunk (2024)

Ploum โ€“ Bikepunk (2024)
0

โ€‹:snugdotmoe:โ€‹ snug.moe maintenance announcement

Thursday, February 19th, at around 17:00 UTC I will bring the server down a couple hours for some maintenance
uhh funny timezone link:
everytimezone.com/s/145565c7 (tho the day is shown a little weird there, don't listen to that part)
Expect the server to be down a couple hours.

And screw it, I may even stream the maintenance ... on
live.cheri.pink/ whatever, because there will be a bunch of wait time while I can do something else like maybe draw on stream or idk.

I want to "defrag" the ZFS pool of the VPS by copying the datasets back and forth from the disk to a temporary one and back so hopefully Netcup's weird way of handling virtual drives is happier.

0
1
0
2
0
1
1
1
1

Apparently it's "security" weakend for me. Feel like all I've accomplished all weekend is security related. New OpenZiti services (want to play with very sandboxed OpenClaw) and making it easier for me to create quick and/or ephemeral api tokens safely in Django apps for agents

0

์ด๋ ‡๊ฒŒ ๋œ ์ด์ƒ, ์‹œ๊ณจ์—์„œ ์—ด์‹ฌํžˆ ๊ทธ๋ ค๋ณผ๊ฒŒ! ..ํ•˜๊ธฐ์—๋„..... ๊ฑฐ๊ธฐ ๊ฐ€๋ฉด ์ œ๋Œ€๋กœ ๊ทธ๋ฆผ ๊ทธ๋ฆด ๊ณต๊ฐ„์กฐ์ฐจ ์—†๋‹จ ๋ง์ด์ฃ ...๐Ÿ˜ฅ

0

AI ๊ฑฐํ’ˆ์€ ํ„ฐ์ง€์ง€ ์•Š์•˜๋‹ค. ์•„์ง๋„ ์„ธ๊ณ„๋Š” AI์˜ ํŠน์ด์ ์— ๋„๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด LLM ๋ชจ๋ธ์„ ํ™•์žฅ ์ค‘์ด๊ณ , ์ด๋กœ ์ธํ•ด ๋žจ๊ฐ’๊ณผ ๊ธˆ, ์€๊ฐ’์€ ๋–จ์–ด์ง€์ง€ ์•Š๊ณ  ์ฒœ์ •๋ถ€์ง€๋กœ ์˜ฌ๋ž๋‹ค. ์„œ๊ธฐ 2036๋…„, ๊ธˆ์ด ์˜จ์Šค๋‹น 2๋งŒ๋‹ฌ๋Ÿฌ๋ฅผ, ์€์ด ์˜จ์Šค๋‹น 2์ฒœ๋‹ฌ๋Ÿฌ์— ๋„๋‹ฌํ•˜๊ณ , DDR5 64๊ธฐ๊ฐ€ ๋žจ์˜ ๊ฐ€๊ฒฉ์ด 1๋งŒ๋‹ฌ๋Ÿฌ์— ๋„๋‹ฌํ•˜์ž. ๊ฑฐ๋ฆฌ์—๋Š” ๊ณผ๊ฑฐ์˜ ์œ ์‚ฐ์„ ์ฐพ์•„ ๋ฐฐํšŒํ•˜๋Š” ์ด๋“ค์ด ๋‚˜ํƒ€๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ๋žŒ๋“ค์€... "์‚ด์•„์žˆ๋Š” ๋†ˆ๋“ค์€ ๋จผ์ง€๋ฅผ ํ„ธ์–ด ์ค‘๊ณ ๋‚˜๋ผ์— ์˜ฌ๋ฆฌ๊ณ , ์ฃฝ์–ด๋ฒ„๋ฆฐ ๋†ˆ๋“ค์€ ๊ธฐํŒ์„ ๋…น์—ฌ ๊ธˆ๊ณผ ์€์„ ์บ๋‚ด๋ผ." "์˜ˆ, ๋ณด์Šค." ๊ทธ๋“ค์„ '๋žจ์ผ€๋นˆ์ €'๋ผ๊ณ  ๋ถˆ๋ €๋‹ค.

RE: https://bsky.app/profile/did:plc:xp5s2asgsq7pfeayl7qrocim/post/3mevvmlwzo22y

0
0
0
0
0
0
0
0

If you're trying to persuade someone to try Mastodon (or Pixelfed, or Friendica, etc), you'll get a lot further explaining them as algorithm-free, ad-free, AI-free, nazi-free conversations, rather than trying to describe a "decentralised protocol" or why "open source" is good.

Mastodon is conversations between real people.

It's sharing.

0
0
0

Federated private groups (Announce vs Add)

julian @julian@activitypub.space

<p><a href="https://utsukta.org/channel/sk">@<bdi>sk@utsukta.org</bdi></a> mentioned <a href="https://utsukta.org/item/b3b12c30-f9fb-4cbf-9276-1cbf1d27f0de" rel="nofollow ugc">in another thread</a> that the way Hubzilla and threadiverse software handle group discussions is incompatible.</p> <p>It got me thinking about whether that is true. At its core both FEPs (171b and 1b12, respectively) rely on a central "distributor" node to send activities to recipients.</p> <p><a href="https://mitra.social/users/silverpill">@<bdi>silverpill@mitra.social</bdi></a> did further comparisons in thr text of 171b itself:</p> <blockquote> <p><code>Announce</code> activity is used instead of <code>Add</code>.</p></blockquote>

Read more โ†’
0
1

์†Œ๋‹ˆ๊ฐ€ ์ž‘๊ณก AI์˜ ํ•™์Šต (์›๊ณก) ๋ฐ์ดํ„ฐ๋ฅผ ํŠน์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ์„ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์›๊ณก์˜ ๊ถŒ๋ฆฌ ๋ณด์œ ์ž๊ฐ€ AI ์„œ๋น„์Šค์ธก์— ์„ค๋ช…, ๋Œ€๊ฐ€๋ฅผ ์š”๊ตฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. www.nikkei.com/article/DGXZ... (์œ ๋ฃŒ ๊ธฐ์‚ฌ)

ใ‚ฝใƒ‹ใƒผใ‚ฐใƒซใƒผใƒ—ใ€ไฝœๆ›ฒAIใฎๅญฆ็ฟ’ใƒ‡ใƒผใ‚ฟใ‚’็‰นๅฎšใ€€ๅ‰ตไฝœ่€…ใธใฎๅฏพไพก็ฎ—...

ใ‚ฝใƒ‹ใƒผใ‚ฐใƒซใƒผใƒ—ใ€ไฝœๆ›ฒAIใฎๅญฆ็ฟ’ใƒ‡ใƒผใ‚ฟใ‚’็‰นๅฎšใ€€ๅ‰ตไฝœ่€…ใธใฎๅฏพไพก็ฎ—ๅ‡บๅฏ่ƒฝใซ - ๆ—ฅๆœฌ็ตŒๆธˆๆ–ฐ่ž

ใ€ใ‚ทใƒชใ‚ณใƒณใƒใƒฌใƒผ=ไธญ่—ค็Žฒใ€‘ใ‚ฝใƒ‹ใƒผใ‚ฐใƒซใƒผใƒ—ใฏไบบๅทฅ็Ÿฅ่ƒฝ๏ผˆAI๏ผ‰ใŒใคใใฃใŸ้Ÿณๆฅฝใ‹ใ‚‰ๅญฆ็ฟ’ใ‚„็”Ÿๆˆใซไฝฟใ‚ใ‚ŒใŸๆฅฝๆ›ฒใ‚’ๅ‰ฒใ‚Šๅ‡บใ™ๆŠ€่ก“ใ‚’้–‹็™บใ—ใŸใ€‚ๆ—ขๅญ˜ใฎๆฅฝๆ›ฒใŒๅˆฉ็”จใ•ใ‚Œใฆใ„ใŸๅ ดๅˆใซใ€AIใฎ้–‹็™บๅ…ƒใซ่ชฌๆ˜Žใ‚„ๅฏพไพกใ‚’ๆฑ‚ใ‚ใ‚‹ใ“ใจใŒๅฏ่ƒฝใซใชใ‚‹ใ€‚ๅŒ็คพใฏ้Ÿณๆฅฝ็”ŸๆˆAIใŒ็”Ÿใฟๅ‡บใ™ๅŽ็›Šใ‚’ใ‚ฏใƒชใ‚จใƒผใ‚ฟใƒผใ‚‰ใซ้…ๅˆ†ใ™ใ‚‹ไป•็ต„ใฟใฅใใ‚Šใซใ‚‚ๅฝน็ซ‹ใคใจใฟใฆใ„ใ‚‹ใ€‚้Ÿณๆฅฝใ‚„ๆ˜ ๅƒใ€ๅ‡บ็‰ˆใชใฉใฎใ‚ณใƒณใƒ†ใƒณใƒ„ๅˆ†้‡Žใงใฏใ€AI้–‹็™บไผๆฅญใซใ‚ˆใฃใฆ่‘—ไฝœ็‰ฉใŒ็„กๆ–ญใงAIใฎๅญฆ็ฟ’ใ‚„็”Ÿๆˆใซ

www.nikkei.com ยท ๆ—ฅๆœฌ็ตŒๆธˆๆ–ฐ่ž

0

ๆดช ๆฐ‘ๆ†™ (Hong Minhee) shared the below article:

์™œ gaji์ธ๊ฐ€? - TS๋กœ ์•ˆ์ „ํ•˜๊ฒŒ GitHub Actions ์ž‘์„ฑํ•˜๊ธฐ

๊ฐœ๋ฐœ๊ณฐ @gaebalgom@hackers.pub

์ตœ๊ทผ์— ์ €๋Š” TS๋กœ GitHub Actions๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ํˆด์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด๋ฆ„ํ•˜์—ฌ GitHub Actions Justified Improvements, gaji ๋ผ๋Š” ํˆด์ž…๋‹ˆ๋‹ค. ์ €๋Š” ์™œ TS๋กœ GitHub Actions๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜์—ˆ์œผ๋ฉฐ, ๊ธฐ์กด ํˆด๋“ค๊ณผ ์–ด๋–ค ์ ์ด ๋‹ค๋ฅผ๊นŒ์š”? ๊ฐ™์ด ์•Œ์•„๋ณด์‹œ์ฃ .

๊ฐ€์ง€ ๊ณต์‹ ๋ฌธ์„œ

Toss Client DevOps Team์—์„œ์˜ ์ธํ„ด ๊ทผ๋ฌด

์˜ฌํ•ด 1์›”๋ถ€ํ„ฐ, ์ €๋Š” Toss Client DevOps Team ์—์„œ ์ธํ„ด ๊ทผ๋ฌด๋ฅผ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. Client DevOps Team์„ ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜์ž๋ฉด, ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ”„๋ผ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๋Š” ํŒ€์ž…๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ฃผ๋กœ ์ง„ํ–‰ํ•œ ์ž‘์—…์€ ๊ธฐ์กด ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ GitHub Actions๋กœ ์ „ํ™˜ํ•˜๊ณ , ์ƒˆ๋กœ์šด ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ์ปค์Šคํ…€ ์•ก์…˜์„ ๋งŒ๋“œ๋Š” ์ผ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์‹ญ ๊ฐœ์˜ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋‹ค๋ฃจ๋ฉด์„œ, ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•œ ๋ฐฐํฌ ์ธํ”„๋ผ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ํŒ€์—์„œ ์ •์ž‘ ๊ทธ ์ธํ”„๋ผ๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ • ์ž์ฒด๋Š” ๋А๋ฆฌ๊ณ  ๋ถˆ์•ˆ์ „ํ•˜๋‹ค๋Š” ๊ฑธ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜คํƒ€ ํ•˜๋‚˜๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด ์ปค๋ฐ‹ โ†’ ํ‘ธ์‹œ โ†’ CI ์‹คํ–‰ โ†’ ์‹คํŒจ ํ™•์ธ์ด๋ผ๋Š” ์‚ฌ์ดํด์„ ๋ฐ˜๋ณตํ•ด์•ผ ํ–ˆ๊ณ , ๋กœ์ปฌ์—์„œ ์žฌํ˜„ํ•  ๋ฐฉ๋ฒ•์ด ์—†์œผ๋‹ˆ git ์‹ค๋ ฅ๋งŒ ๋Š˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ธํ„ด์„ ํ•˜๋ฉฐ ์ž๋ฆฌ์žก์€ ์ƒ๊ฐ๋“ค

์ด๋Ÿฐ ์ธํ„ด ๊ทผ๋ฌด๋ฅผ ํ•˜๋ฉด์„œ, ๋ช‡๊ฐ€์ง€ ์ƒ๊ฐ์ด ์ž๋ฆฌ์žก์•˜์Šต๋‹ˆ๋‹ค. ์ฒ ํ•™๊นŒ์ง€๋Š” ์•„๋‹ˆ๊ณ , ๋‹จ์ˆœํ•œ ์ƒ๊ฐ ์ •๋„์ž…๋‹ˆ๋‹ค.

  1. ์ž…์ถœ๋ ฅ์ด ๋ช…ํ™•ํ•ด์•ผ ์ข‹์€ ์†Œํ”„ํŠธ์›จ์–ด์ž…๋‹ˆ๋‹ค.

  2. YAML์€ ๋™์ž‘์„ ํ‘œํ˜„ํ•  ์–ธ์–ด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. Actions๋Š” ์ž…์ถœ๋ ฅ๊ณผ ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๊ฐ€ ์žˆ๋Š” ๋™์ž‘์ž…๋‹ˆ๋‹ค. ์ด๊ฑธ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์–ธ์–ด์ธ YAML๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ์–ธ์–ด์˜ ์‚ฌ์šฉ์ฒ˜๊ฐ€ ์ž˜๋ชป๋œ ๊ฒƒ์ด ์•„๋‹๊นŒ์š”? ์„ ์–ธ์ ์ด์ง€ ์•Š์€ ๊ฑธ ์„ ์–ธ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ, YAML ์•ˆ์— ์…ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋„ฃ๋Š” ๊ธฐํ˜•์ ์ธ ๊ตฌ์กฐ๊ฐ€ ๋˜์–ด๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค.

  3. ์–ด๋А ํ™˜๊ฒฝ์—์„œ๋“  ์žฌํ˜„ ๊ฐ€๋Šฅํ•ด์•ผ ์ข‹์€ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

gaji๋Š” ์ด ์ค‘ 1, 2๋ฒˆ์—์„œ ์ถœ๋ฐœํ–ˆ๊ณ , 3๋ฒˆ์€ act ๊ฐ™์€ ๋„๊ตฌ์˜ ์˜์—ญ์ž…๋‹ˆ๋‹ค.

GitHub Actions์˜ 3๊ฐ€์ง€ ๊ตฌ์กฐ์  ๋ฌธ์ œ

์œ„ ์ƒ๊ฐ์„ ๊ฐ€์ง€๊ณ  GitHub Actions๋ฅผ ๋ณด๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. YAML์€ ๋ฐ์ดํ„ฐ ํ‘œํ˜„ ์–ธ์–ด์ง€, ๋™์ž‘์„ ํ‘œํ˜„ํ•˜๊ธฐ์— ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  2. ํƒ€์ž… ๊ฒ€์‚ฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์ €์žฅ์†Œ์— ์˜์กดํ•  ์ผ์ด ๋งŽ์€๋ฐ(actions/checkout@v5์กฐ์ฐจ ์™ธ๋ถ€ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค), ์ด๋“ค์ด ์š”๊ตฌํ•˜๋Š” ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋ฌธ์„œ๋ฅผ ๋ณด๊ณ  ์ผ์ผ์ด ํ˜•์‹์— ๋งž๊ฒŒ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋กœ์ปฌ์—์„œ ์žฌํ˜„ํ•˜๊ธฐ๊ฐ€ ํž˜๋“ญ๋‹ˆ๋‹ค.

์ด ์„ธ ๊ฐ€์ง€๊ฐ€ ๊ฒฐํ•ฉํ•ด GitHub Actions๋Š” ์‹คํ–‰ํ•˜๊ธฐ ์ „๊นŒ์ง€ ๊ฐ„๋‹จํ•œ ์˜คํƒ€ ํ•˜๋‚˜๋„ ๋ชป ์ฐพ๋Š” ํ”Œ๋žซํผ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

name: CI
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-node@v4
        with:
          node-versoin: '20'  # ํ‚ค ์ด๋ฆ„ ์˜คํƒ€! ๋Ÿฐํƒ€์ž„๊นŒ์ง€ ์˜ค๋ฅ˜ ์—†์Œ โŒ
          cache: 'npm'

      - run: npm ci
      - run: npm test

gaji๋Š” ์ฒซ ๋ฒˆ์งธ์™€ ๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด ๋„๊ตฌ๋“ค๊ณผ์˜ ๋น„๊ต

actionlint

์†”์งํžˆ ๋งํ•˜๋ฉด, gaji๋ฅผ ๋งŒ๋“ค ๋‹น์‹œ์—๋Š” actionlint์˜ ์กด์žฌ๋ฅผ ๋ชฐ๋ž์Šต๋‹ˆ๋‹ค. ์ดํ›„์— ์•Œ๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ, ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ${{ }} ํ‘œํ˜„์‹์˜ ํƒ€์ž… ์ฒดํฌ, ์•ก์…˜ ์ž…๋ ฅ ๊ฒ€์ฆ, shellcheck ํ†ตํ•ฉ ๋“ฑ YAML ์›Œํฌํ”Œ๋กœ์šฐ์˜ ์˜ค๋ฅ˜๋ฅผ ์ƒ๋‹นํžˆ ์ž˜ ์žก์•„์ค๋‹ˆ๋‹ค.

๋‹ค๋งŒ ๊ทผ๋ณธ์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. actionlint๋Š” YAML์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์‚ฌํ›„์— ์˜ค๋ฅ˜๋ฅผ ์žก๋Š” ๋ฆฐํ„ฐ์ด๊ณ , gaji๋Š” YAML ์ž์ฒด๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์ž‘์„ฑ ์‹œ์ ์— ์˜ค๋ฅ˜๋ฅผ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์ ‘๊ทผ์ž…๋‹ˆ๋‹ค. ๋ฆฐํ„ฐ๋Š” "์‹ค์ˆ˜๋ฅผ ์•Œ๋ ค์ฃผ๊ณ ", ํƒ€์ž… ์‹œ์Šคํ…œ์€ "์‹ค์ˆ˜๋ฅผ ํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค." ๊ฐœ๋ฐœ ๊ฒฝํ—˜ ์ธก๋ฉด์—์„œ๋„, actionlint๋Š” ๋ณ„๋„ CLI๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์—๋””ํ„ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•ด์•ผ ํ•˜์ง€๋งŒ, gaji๋Š” TypeScript ๋„ค์ดํ‹ฐ๋ธŒ ์ž๋™์™„์„ฑ๊ณผ ์ธ๋ผ์ธ ํƒ€์ž… ํžŒํŠธ๊ฐ€ ์—๋””ํ„ฐ์—์„œ ์ฆ‰์‹œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋‘˜์„ ๊ฐ™์ด ์“ฐ๋ฉด ๋”์šฑ ์ข‹์Šต๋‹ˆ๋‹ค. gaji๊ฐ€ ์ƒ์„ฑํ•œ YAML์„ actionlint๋กœ ๊ฒ€์ฆํ•˜๋ฉด ๊ฐ€์žฅ ์ด์ƒ์ ์ธ ์กฐํ•ฉ์ด ๋ฉ๋‹ˆ๋‹ค. gaji๊ฐ€ TypeScript ๋‹จ์—์„œ ์•ก์…˜ ์ž…๋ ฅ์˜ ํƒ€์ž…์„ ์žก๊ณ , actionlint๊ฐ€ ${{ }} ํ‘œํ˜„์‹ ๊ฒ€์ฆ ๊ฐ™์€ YAML ๋‹จ์˜ ๊ฒ€์‚ฌ๋ฅผ ๋ณด์™„ํ•ฉ๋‹ˆ๋‹ค.

emmanuelnk/github-actions-workflow-ts

github-actions-workflow-ts๋Š” TS๋กœ GitHub Actions๋ฅผ ํ‘œ๊ธฐํ•œ๋‹ค๋Š” ์•„์ด๋””์–ด์˜ ์ถœ๋ฐœ์ ์ด ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. action.yml์—์„œ ํƒ€์ž…์„ ์ž๋™ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์•„์ด๋””์–ด ์ž์ฒด๋Š” gaji์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ฝ”๋“œ์  ์˜ ์ฃผ์ฒด๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. github-actions-workflow-ts๋Š” ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ trackedActions ๋ชฉ๋ก์„ ๊ด€๋ฆฌํ•˜์—ฌ npm ํŒจํ‚ค์ง€๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ์‹์ด๊ณ , gaji๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ์•ก์…˜์— ๋Œ€ํ•ด ์ฆ‰์‹œ ๋กœ์ปฌ์—์„œ ํƒ€์ž…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

github-actions-workflow-ts์˜ ์žฅ์ ์€ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. npm ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋ฉด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ„๋„์˜ ์ฝ”๋“œ์  ์„ ์‹คํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. step outputs์— ๋Œ€ํ•œ ํƒ€์ž… ์•ˆ์ „์„ฑ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด ๋‹จ์ ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ชฉ๋ก์— ์žˆ๋Š” ์•ก์…˜๋งŒ ํƒ€์ž…์ด ์ง€์›๋˜๋ฏ€๋กœ, ์ปค์Šคํ…€ ์•ก์…˜์ด๋‚˜ GHE ๋‚ด๋ถ€ ์•ก์…˜์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ƒˆ ์•ก์…˜์ด๋‚˜ ๋ฒ„์ „ ์ถ”๊ฐ€๋„ ๋ฉ”์ธํ…Œ์ด๋„ˆ์— ์˜์กดํ•˜๊ณ , ์™ธ๋ถ€ JS ๋Ÿฐํƒ€์ž„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

gaji์˜ ์žฅ์ ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ์–ด๋–ค ์•ก์…˜์ด๋“  ์ฆ‰์‹œ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ปค์Šคํ…€ ์•ก์…˜์ด๋“  GHE ๋‚ด๋ถ€ ์•ก์…˜์ด๋“  ์ƒ๊ด€์—†์Šต๋‹ˆ๋‹ค. Rust ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ JS ๋Ÿฐํƒ€์ž„๋„ ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ๋‹จ์ ์œผ๋กœ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ gaji dev๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํƒ€์ž…์ด ์ƒ์„ฑ๋˜๊ณ , ํƒ€์ž…์ด ๋กœ์ปฌ์—์„œ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ์„ธํŒ…์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” GHE ํ™˜๊ฒฝ์—์„œ ์ˆ˜๋งŽ์€ ์ปค์Šคํ…€ ์•ก์…˜์„ ๋‹ค๋ค˜๋˜ ๊ฒฝํ—˜ ๋•Œ๋ฌธ์—, gaji ์ชฝ์˜ ์ ‘๊ทผ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

gaji์˜ ์ ‘๊ทผ๋ฒ•

์™œ ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์—ˆ๋Š”๊ฐ€

์™œ Rust์ธ๊ฐ€? ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋นŒ๋“œ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ์†๋„๋ฅผ ์ด์•ผ๊ธฐํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. clippy, rustfmt ๋“ฑ ์—ฌ๋Ÿฌ ๊ฒ€์‚ฌ ๋„๊ตฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์–ด์„œ LLM์„ ์ด์šฉํ•œ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ํฌ๊ฒŒ ์ค„์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋•๋ถ„์— ์ธํ„ด์„ ํ•˜๋ฉด์„œ๋„ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ oxc ๋“ฑ Rust๋กœ ์ž‘์„ฑ๋œ TypeScript ์ง€์› ๋„๊ตฌ๋“ค์ด ์ด๋ฏธ ์„ฑ์ˆ™ํ•ด ์žˆ์–ด์„œ, Rust์—์„œ TypeScript๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒƒ ๋˜ํ•œ ํŽธํ–ˆ์Šต๋‹ˆ๋‹ค.

์™œ TypeScript์ธ๊ฐ€? ์šฐ์„  ์ œ๊ฐ€ JS/TS ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. TypeScript์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์€ ๊ฐ•๋ ฅํ•˜๋ฉด์„œ๋„ ๋ณดํŽธ์ ์ด๋ผ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ด๋ฏธ ์ต์ˆ™ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  GitHub Actions์˜ YAML ๊ตฌ์กฐ๊ฐ€ ๋ณธ์งˆ์ ์œผ๋กœ JSON๊ณผ ์œ ์‚ฌํ•˜๋ฏ€๋กœ, TS/JS์—์„œ JSON-like ๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋‹จ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์€ ๋ชจ๋“  gaji ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ์ด ๊ทธ ์ž์ฒด๋กœ ์œ ํšจํ•œ TS ํŒŒ์ผ์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. Deno์ฒ˜๋Ÿผ TS๋ฅผ ๋ฐ”๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์—์„œ gaji๋กœ ์ž‘์„ฑ๋œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ํ•ด๋‹น ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ JSON์œผ๋กœ ํ‘œํ˜„ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

์™œ action.yml ์ž๋™ ์ฝ”๋“œ์  ์ธ๊ฐ€? Client DevOps Team์—์„œ ์ปค์Šคํ…€ ์•ก์…˜์„ ์ž‘์„ฑํ•˜๋Š” ์ผ์„ ํ–ˆ๊ณ , ์ด๋ฏธ ์ƒ๋‹นํžˆ ๋งŽ์€ ์ปค์Šคํ…€ ์•ก์…˜์ด ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์˜ ๋ฌธ์„œ๋ฅผ ์ผ์ผ์ด ๋ณด๋ฉฐ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ํž˜๋“ค์—ˆ๋˜ ๊ฒฝํ—˜์ด ์ง์ ‘์ ์ธ ๋™๊ธฐ์˜€์Šต๋‹ˆ๋‹ค. Hackers.pub์— ๊ธฐ์—ฌํ•˜๋ฉด์„œ GraphQL ์ž๋™ ์ฝ”๋“œ์  ์˜ ๊ฐœ๋…์„ ์ ‘ํ–ˆ๊ณ , ๊ฐ™์€ ์ ‘๊ทผ์„ GitHub Actions์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ๊ตฌ์กฐ

gaji ์›Œํฌํ”Œ๋กœ์šฐ๋Š” getAction() โ†’ Job โ†’ Workflow โ†’ .build()์˜ ํ๋ฆ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. gaji dev --watch๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ƒˆ ์•ก์…˜ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ž๋™์œผ๋กœ ํƒ€์ž…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

import { getAction, Job, Workflow } from "../generated/index.js";

const checkout = getAction("actions/checkout@v5");
const setupNode = getAction("actions/setup-node@v4");

const build = new Job("ubuntu-latest")
  .addStep(checkout({}))
  .addStep(setupNode({
    with: {
      "node-version": "20",
      cache: "npm",
    },
  }))
  .addStep({ run: "npm ci" })
  .addStep({ run: "npm test" });

const workflow = new Workflow({
  name: "CI",
  on: { push: { branches: ["main"] } },
}).addJob("build", build);

workflow.build("ci");

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ๋ชจ๋“  ์•ก์…˜ ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ž๋™์™„์„ฑ, ์ปดํŒŒ์ผ ์‹œ์  ํƒ€์ž… ์ฒดํฌ, action.yml ๋ฌธ์„œ์˜ IDE ํžŒํŠธ ํ‘œ์‹œ, ๊ธฐ๋ณธ๊ฐ’ ํ‘œ์‹œ๊ฐ€ ๋ชจ๋‘ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. CompositeJob์œผ๋กœ ๊ณตํ†ต ๋กœ์ง์„ ํด๋ž˜์Šค๋กœ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, CallJob์œผ๋กœ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋„ TS ์ฝ”๋“œ์ƒ์—์„  ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€: gaji ์ž์ฒด์˜ ๋ฆด๋ฆฌ์ฆˆ CD

gaji๋Š” ๋ชจ๋“  ci/cd๋ฅผ gaji๋กœ ์ž‘์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ์ค‘์—์„œ ์ œ์ผ ๋ณต์žกํ•œ release.ts๋Š” 4๊ฐœ์˜ Job์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • build: 5๊ฐœ ํ”Œ๋žซํผ(linux-x64, linux-arm64, darwin-x64, darwin-arm64, win32-x64) ํฌ๋กœ์Šค ๋นŒ๋“œ
  • upload-release-assets: GitHub Release์— ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ ์ฒดํฌ์„ฌ ์—…๋กœ๋“œ
  • publish-npm: npm์— ํ”Œ๋žซํผ๋ณ„ ํŒจํ‚ค์ง€ ํผ๋ธ”๋ฆฌ์‹œ
  • publish-crates: crates.io์— OIDC ๊ธฐ๋ฐ˜ ํผ๋ธ”๋ฆฌ์‹œ

์ด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ YAML๋กœ ์ปดํŒŒ์ผ๋˜๋ฉด ์•ฝ 180์ค„์˜ ํ‰ํƒ„ํ•œ ๊ตฌ์กฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ฃผ์„ ์—†์ด๋Š” Job ๊ฐ„ ๊ฒฝ๊ณ„๋‚˜ ์˜์กด๊ด€๊ณ„๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. TypeScript ๋ฒ„์ „์—์„œ๋Š” build, uploadReleaseAssets, publishNpm, publishCrates๋ผ๋Š” ๋ณ€์ˆ˜๋ช…๋งŒ์œผ๋กœ ๊ตฌ์กฐ๊ฐ€ ์ฆ‰์‹œ ํŒŒ์•…๋ฉ๋‹ˆ๋‹ค. 6๊ฐœ์˜ ์™ธ๋ถ€ ์•ก์…˜์„ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ , ๋ณต์žกํ•œ ๋งคํŠธ๋ฆญ์Šค ๋นŒ๋“œ์™€ OS๋ณ„ ๋ถ„๊ธฐ๊ฐ€ ์ฝ”๋“œ ๊ตฌ์กฐ ์•ˆ์—์„œ ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

gaji์˜ ํ•œ๊ณ„

gaji์—๋Š” ์—ฌ์ „ํžˆ ํ•œ๊ณ„๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๊ทผ๋ณธ์ ์œผ๋กœ, ์ตœ์ข… ์‚ฐ์ถœ๋ฌผ์ด ์—ฌ์ „ํžˆ YAML์ž…๋‹ˆ๋‹ค. GitHub Actions ํ”Œ๋žซํผ์˜ ์ž…๋ ฅ ํ˜•์‹์ด YAML์ธ ์ด์ƒ, gaji๋„ ๊ทธ ์ œ์•ฝ ์•ˆ์— ๊ฐ‡ํž™๋‹ˆ๋‹ค. gaji๋Š” ์ด ํ”Œ๋žซํผ ์œ„์—์„œ์˜ ์ตœ์„ ์ด์ง€, ์ด์ƒ์ ์ธ ํ•ด๋‹ต์€ ์•„๋‹™๋‹ˆ๋‹ค. ์›๋ณธ action.yml์˜ inputs๊ฐ€ ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž ์ •๋„๋กœ๋งŒ ํ‘œํ˜„๋˜๊ธฐ ๋•Œ๋ฌธ์—, "npm" | "yarn" | "pnpm" ๊ฐ™์€ ์„ธ๋ฐ€ํ•œ ๊ฐ’ ์ˆ˜์ค€์˜ ํƒ€์ž…๊นŒ์ง€๋Š” ์ œ๊ณตํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ${{ matrix.target.rust_target }} ๊ฐ™์€ GitHub Actions ํ‘œํ˜„์‹๋„ ์—ฌ์ „ํžˆ ์ˆœ์ˆ˜ ๋ฌธ์ž์—ด์ด๋ผ ํƒ€์ž… ๊ฒ€์ฆ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์ˆ ์ ์ธ ์ œํ•œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. gaji dev๋Š” getAction()์„ ์ •์  ๋ถ„์„ํ•ด์„œ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด๋งŒ ์ง€์›ํ•˜๊ณ , ๋ณ€์ˆ˜๋‚˜ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด ๊ฐ’ ์ž์ฒด์˜ ์˜คํƒ€(cache: "npn" vs cache: "npm")๋„ ์žก์„ ์ˆ˜ ์—†๊ณ ์š”.

์•ž์œผ๋กœ์˜ ๋ฐฉํ–ฅ

gaji์˜ ํ˜„์žฌ ์•„ํ‚คํ…์ฒ˜๋Š” TypeScript โ†’ Parse (oxc) โ†’ Execute (QuickJS) โ†’ YAML์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ์  ์„ ๋งŒ๋“ค๋ฉด์„œ ํ•œ ๊ฐ€์ง€ ์•„์ด๋””์–ด๋ฅผ ์–ป์—ˆ๋Š”๋ฐ, ํ”„๋ก ํŠธ์—”๋“œ(์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ์–ธ์–ด)์™€ ๋ฐฑ์—”๋“œ(YAML ์ƒ์„ฑ)๋ฅผ ์ž˜ ๋ถ„๋ฆฌํ•˜๊ณ , ์ค‘๊ฐ„ ์–ธ์–ด๋ฅผ ์ž˜ ์ •์˜ํ•˜๋ฉด TypeScript ์™ธ์˜ ๋‹ค๋ฅธ ์–ธ์–ด๋กœ๋„ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

1.0์—์„œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ์„ ๋„์ž…ํ•ด ๋‹ค๋ฅธ ์–ธ์–ด ์ง€์›์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ๊ณ„ํš์ž…๋‹ˆ๋‹ค. gaji์˜ ํ•ต์‹ฌ ๊ฐ€์น˜์ธ action.yml ์ž๋™ ํƒ€์ž… ์ƒ์„ฑ์„ TypeScript์— ๊ตญํ•œํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

Special Thanks

gaji ๋ธŒ๋žœ๋“œ

์ด๋ฆ„ ์ œ์•ˆ์„ ํ•ด ์ฃผ์‹  kiwiyou ๋‹˜, RanolP ๋‹˜, ๋กœ๊ณ  ์ œ์ž‘์„ ํ•ด ์ฃผ์‹  sij411 ๋‹˜๊ป˜ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. Client DevOps Team์—๊ฒŒ๋„ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒ€์—์„œ ๊ฒช์€ ๊ฒฝํ—˜์ด ์•„๋‹ˆ์—ˆ์œผ๋ฉด YAML๊ณผ GitHub Actions์— ๋Œ€ํ•ด ์ƒ๊ฐํ•ด ๋ณด์ง€ ์•Š์•˜์„ ๊ฒ๋‹ˆ๋‹ค. emmanuelnk/github-actions-workflow-ts์—๊ฒŒ๋„ ๊ฐ์‚ฌ๋ฅผ ํ‘œํ•ฉ๋‹ˆ๋‹ค. TS๋กœ GitHub Actions๋ฅผ ํ‘œ๊ธฐํ•œ๋‹ค๋Š” ์•„์ด๋””์–ด์™€ ๊ธฐ๋ณธ์ ์ธ TS API ์„ค๊ณ„๋Š” ์—ฌ๊ธฐ์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

Read more โ†’
2
0
1

์™œ gaji์ธ๊ฐ€? - TS๋กœ ์•ˆ์ „ํ•˜๊ฒŒ GitHub Actions ์ž‘์„ฑํ•˜๊ธฐ

๊ฐœ๋ฐœ๊ณฐ @gaebalgom@hackers.pub

์ตœ๊ทผ์— ์ €๋Š” TS๋กœ GitHub Actions๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ํˆด์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด๋ฆ„ํ•˜์—ฌ GitHub Actions Justified Improvements, gaji ๋ผ๋Š” ํˆด์ž…๋‹ˆ๋‹ค. ์ €๋Š” ์™œ TS๋กœ GitHub Actions๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜์—ˆ์œผ๋ฉฐ, ๊ธฐ์กด ํˆด๋“ค๊ณผ ์–ด๋–ค ์ ์ด ๋‹ค๋ฅผ๊นŒ์š”? ๊ฐ™์ด ์•Œ์•„๋ณด์‹œ์ฃ .

๊ฐ€์ง€ ๊ณต์‹ ๋ฌธ์„œ

Toss Client DevOps Team์—์„œ์˜ ์ธํ„ด ๊ทผ๋ฌด

์˜ฌํ•ด 1์›”๋ถ€ํ„ฐ, ์ €๋Š” Toss Client DevOps Team ์—์„œ ์ธํ„ด ๊ทผ๋ฌด๋ฅผ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. Client DevOps Team์„ ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜์ž๋ฉด, ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ”„๋ผ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๋Š” ํŒ€์ž…๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ฃผ๋กœ ์ง„ํ–‰ํ•œ ์ž‘์—…์€ ๊ธฐ์กด ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ GitHub Actions๋กœ ์ „ํ™˜ํ•˜๊ณ , ์ƒˆ๋กœ์šด ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ์ปค์Šคํ…€ ์•ก์…˜์„ ๋งŒ๋“œ๋Š” ์ผ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์‹ญ ๊ฐœ์˜ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋‹ค๋ฃจ๋ฉด์„œ, ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•œ ๋ฐฐํฌ ์ธํ”„๋ผ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ํŒ€์—์„œ ์ •์ž‘ ๊ทธ ์ธํ”„๋ผ๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ • ์ž์ฒด๋Š” ๋А๋ฆฌ๊ณ  ๋ถˆ์•ˆ์ „ํ•˜๋‹ค๋Š” ๊ฑธ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜คํƒ€ ํ•˜๋‚˜๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด ์ปค๋ฐ‹ โ†’ ํ‘ธ์‹œ โ†’ CI ์‹คํ–‰ โ†’ ์‹คํŒจ ํ™•์ธ์ด๋ผ๋Š” ์‚ฌ์ดํด์„ ๋ฐ˜๋ณตํ•ด์•ผ ํ–ˆ๊ณ , ๋กœ์ปฌ์—์„œ ์žฌํ˜„ํ•  ๋ฐฉ๋ฒ•์ด ์—†์œผ๋‹ˆ git ์‹ค๋ ฅ๋งŒ ๋Š˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ธํ„ด์„ ํ•˜๋ฉฐ ์ž๋ฆฌ์žก์€ ์ƒ๊ฐ๋“ค

์ด๋Ÿฐ ์ธํ„ด ๊ทผ๋ฌด๋ฅผ ํ•˜๋ฉด์„œ, ๋ช‡๊ฐ€์ง€ ์ƒ๊ฐ์ด ์ž๋ฆฌ์žก์•˜์Šต๋‹ˆ๋‹ค. ์ฒ ํ•™๊นŒ์ง€๋Š” ์•„๋‹ˆ๊ณ , ๋‹จ์ˆœํ•œ ์ƒ๊ฐ ์ •๋„์ž…๋‹ˆ๋‹ค.

  1. ์ž…์ถœ๋ ฅ์ด ๋ช…ํ™•ํ•ด์•ผ ์ข‹์€ ์†Œํ”„ํŠธ์›จ์–ด์ž…๋‹ˆ๋‹ค.

  2. YAML์€ ๋™์ž‘์„ ํ‘œํ˜„ํ•  ์–ธ์–ด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. Actions๋Š” ์ž…์ถœ๋ ฅ๊ณผ ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๊ฐ€ ์žˆ๋Š” ๋™์ž‘์ž…๋‹ˆ๋‹ค. ์ด๊ฑธ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์–ธ์–ด์ธ YAML๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ์–ธ์–ด์˜ ์‚ฌ์šฉ์ฒ˜๊ฐ€ ์ž˜๋ชป๋œ ๊ฒƒ์ด ์•„๋‹๊นŒ์š”? ์„ ์–ธ์ ์ด์ง€ ์•Š์€ ๊ฑธ ์„ ์–ธ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ, YAML ์•ˆ์— ์…ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋„ฃ๋Š” ๊ธฐํ˜•์ ์ธ ๊ตฌ์กฐ๊ฐ€ ๋˜์–ด๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค.

  3. ์–ด๋А ํ™˜๊ฒฝ์—์„œ๋“  ์žฌํ˜„ ๊ฐ€๋Šฅํ•ด์•ผ ์ข‹์€ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

gaji๋Š” ์ด ์ค‘ 1, 2๋ฒˆ์—์„œ ์ถœ๋ฐœํ–ˆ๊ณ , 3๋ฒˆ์€ act ๊ฐ™์€ ๋„๊ตฌ์˜ ์˜์—ญ์ž…๋‹ˆ๋‹ค.

GitHub Actions์˜ 3๊ฐ€์ง€ ๊ตฌ์กฐ์  ๋ฌธ์ œ

์œ„ ์ƒ๊ฐ์„ ๊ฐ€์ง€๊ณ  GitHub Actions๋ฅผ ๋ณด๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. YAML์€ ๋ฐ์ดํ„ฐ ํ‘œํ˜„ ์–ธ์–ด์ง€, ๋™์ž‘์„ ํ‘œํ˜„ํ•˜๊ธฐ์— ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  2. ํƒ€์ž… ๊ฒ€์‚ฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์ €์žฅ์†Œ์— ์˜์กดํ•  ์ผ์ด ๋งŽ์€๋ฐ(actions/checkout@v5์กฐ์ฐจ ์™ธ๋ถ€ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค), ์ด๋“ค์ด ์š”๊ตฌํ•˜๋Š” ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์ด ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋ฌธ์„œ๋ฅผ ๋ณด๊ณ  ์ผ์ผ์ด ํ˜•์‹์— ๋งž๊ฒŒ ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋กœ์ปฌ์—์„œ ์žฌํ˜„ํ•˜๊ธฐ๊ฐ€ ํž˜๋“ญ๋‹ˆ๋‹ค.

์ด ์„ธ ๊ฐ€์ง€๊ฐ€ ๊ฒฐํ•ฉํ•ด GitHub Actions๋Š” ์‹คํ–‰ํ•˜๊ธฐ ์ „๊นŒ์ง€ ๊ฐ„๋‹จํ•œ ์˜คํƒ€ ํ•˜๋‚˜๋„ ๋ชป ์ฐพ๋Š” ํ”Œ๋žซํผ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

name: CI
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-node@v4
        with:
          node-versoin: '20'  # ํ‚ค ์ด๋ฆ„ ์˜คํƒ€! ๋Ÿฐํƒ€์ž„๊นŒ์ง€ ์˜ค๋ฅ˜ ์—†์Œ โŒ
          cache: 'npm'

      - run: npm ci
      - run: npm test

gaji๋Š” ์ฒซ ๋ฒˆ์งธ์™€ ๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด ๋„๊ตฌ๋“ค๊ณผ์˜ ๋น„๊ต

actionlint

์†”์งํžˆ ๋งํ•˜๋ฉด, gaji๋ฅผ ๋งŒ๋“ค ๋‹น์‹œ์—๋Š” actionlint์˜ ์กด์žฌ๋ฅผ ๋ชฐ๋ž์Šต๋‹ˆ๋‹ค. ์ดํ›„์— ์•Œ๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ, ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ${{ }} ํ‘œํ˜„์‹์˜ ํƒ€์ž… ์ฒดํฌ, ์•ก์…˜ ์ž…๋ ฅ ๊ฒ€์ฆ, shellcheck ํ†ตํ•ฉ ๋“ฑ YAML ์›Œํฌํ”Œ๋กœ์šฐ์˜ ์˜ค๋ฅ˜๋ฅผ ์ƒ๋‹นํžˆ ์ž˜ ์žก์•„์ค๋‹ˆ๋‹ค.

๋‹ค๋งŒ ๊ทผ๋ณธ์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. actionlint๋Š” YAML์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์‚ฌํ›„์— ์˜ค๋ฅ˜๋ฅผ ์žก๋Š” ๋ฆฐํ„ฐ์ด๊ณ , gaji๋Š” YAML ์ž์ฒด๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์ž‘์„ฑ ์‹œ์ ์— ์˜ค๋ฅ˜๋ฅผ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์ ‘๊ทผ์ž…๋‹ˆ๋‹ค. ๋ฆฐํ„ฐ๋Š” "์‹ค์ˆ˜๋ฅผ ์•Œ๋ ค์ฃผ๊ณ ", ํƒ€์ž… ์‹œ์Šคํ…œ์€ "์‹ค์ˆ˜๋ฅผ ํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค." ๊ฐœ๋ฐœ ๊ฒฝํ—˜ ์ธก๋ฉด์—์„œ๋„, actionlint๋Š” ๋ณ„๋„ CLI๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์—๋””ํ„ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•ด์•ผ ํ•˜์ง€๋งŒ, gaji๋Š” TypeScript ๋„ค์ดํ‹ฐ๋ธŒ ์ž๋™์™„์„ฑ๊ณผ ์ธ๋ผ์ธ ํƒ€์ž… ํžŒํŠธ๊ฐ€ ์—๋””ํ„ฐ์—์„œ ์ฆ‰์‹œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋‘˜์„ ๊ฐ™์ด ์“ฐ๋ฉด ๋”์šฑ ์ข‹์Šต๋‹ˆ๋‹ค. gaji๊ฐ€ ์ƒ์„ฑํ•œ YAML์„ actionlint๋กœ ๊ฒ€์ฆํ•˜๋ฉด ๊ฐ€์žฅ ์ด์ƒ์ ์ธ ์กฐํ•ฉ์ด ๋ฉ๋‹ˆ๋‹ค. gaji๊ฐ€ TypeScript ๋‹จ์—์„œ ์•ก์…˜ ์ž…๋ ฅ์˜ ํƒ€์ž…์„ ์žก๊ณ , actionlint๊ฐ€ ${{ }} ํ‘œํ˜„์‹ ๊ฒ€์ฆ ๊ฐ™์€ YAML ๋‹จ์˜ ๊ฒ€์‚ฌ๋ฅผ ๋ณด์™„ํ•ฉ๋‹ˆ๋‹ค.

emmanuelnk/github-actions-workflow-ts

github-actions-workflow-ts๋Š” TS๋กœ GitHub Actions๋ฅผ ํ‘œ๊ธฐํ•œ๋‹ค๋Š” ์•„์ด๋””์–ด์˜ ์ถœ๋ฐœ์ ์ด ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. action.yml์—์„œ ํƒ€์ž…์„ ์ž๋™ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์•„์ด๋””์–ด ์ž์ฒด๋Š” gaji์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ฝ”๋“œ์  ์˜ ์ฃผ์ฒด๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. github-actions-workflow-ts๋Š” ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ trackedActions ๋ชฉ๋ก์„ ๊ด€๋ฆฌํ•˜์—ฌ npm ํŒจํ‚ค์ง€๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ์‹์ด๊ณ , gaji๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ์•ก์…˜์— ๋Œ€ํ•ด ์ฆ‰์‹œ ๋กœ์ปฌ์—์„œ ํƒ€์ž…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

github-actions-workflow-ts์˜ ์žฅ์ ์€ ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. npm ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋ฉด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ„๋„์˜ ์ฝ”๋“œ์  ์„ ์‹คํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. step outputs์— ๋Œ€ํ•œ ํƒ€์ž… ์•ˆ์ „์„ฑ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด ๋‹จ์ ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ชฉ๋ก์— ์žˆ๋Š” ์•ก์…˜๋งŒ ํƒ€์ž…์ด ์ง€์›๋˜๋ฏ€๋กœ, ์ปค์Šคํ…€ ์•ก์…˜์ด๋‚˜ GHE ๋‚ด๋ถ€ ์•ก์…˜์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ƒˆ ์•ก์…˜์ด๋‚˜ ๋ฒ„์ „ ์ถ”๊ฐ€๋„ ๋ฉ”์ธํ…Œ์ด๋„ˆ์— ์˜์กดํ•˜๊ณ , ์™ธ๋ถ€ JS ๋Ÿฐํƒ€์ž„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

gaji์˜ ์žฅ์ ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ์–ด๋–ค ์•ก์…˜์ด๋“  ์ฆ‰์‹œ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ปค์Šคํ…€ ์•ก์…˜์ด๋“  GHE ๋‚ด๋ถ€ ์•ก์…˜์ด๋“  ์ƒ๊ด€์—†์Šต๋‹ˆ๋‹ค. Rust ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ JS ๋Ÿฐํƒ€์ž„๋„ ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ๋‹จ์ ์œผ๋กœ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ gaji dev๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํƒ€์ž…์ด ์ƒ์„ฑ๋˜๊ณ , ํƒ€์ž…์ด ๋กœ์ปฌ์—์„œ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ์„ธํŒ…์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” GHE ํ™˜๊ฒฝ์—์„œ ์ˆ˜๋งŽ์€ ์ปค์Šคํ…€ ์•ก์…˜์„ ๋‹ค๋ค˜๋˜ ๊ฒฝํ—˜ ๋•Œ๋ฌธ์—, gaji ์ชฝ์˜ ์ ‘๊ทผ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

gaji์˜ ์ ‘๊ทผ๋ฒ•

์™œ ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์—ˆ๋Š”๊ฐ€

์™œ Rust์ธ๊ฐ€? ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋นŒ๋“œ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ์˜ ์†๋„๋ฅผ ์ด์•ผ๊ธฐํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. clippy, rustfmt ๋“ฑ ์—ฌ๋Ÿฌ ๊ฒ€์‚ฌ ๋„๊ตฌ๊ฐ€ ๋‚ด์žฅ๋˜์–ด ์žˆ์–ด์„œ LLM์„ ์ด์šฉํ•œ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ํฌ๊ฒŒ ์ค„์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋•๋ถ„์— ์ธํ„ด์„ ํ•˜๋ฉด์„œ๋„ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ oxc ๋“ฑ Rust๋กœ ์ž‘์„ฑ๋œ TypeScript ์ง€์› ๋„๊ตฌ๋“ค์ด ์ด๋ฏธ ์„ฑ์ˆ™ํ•ด ์žˆ์–ด์„œ, Rust์—์„œ TypeScript๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฒƒ ๋˜ํ•œ ํŽธํ–ˆ์Šต๋‹ˆ๋‹ค.

์™œ TypeScript์ธ๊ฐ€? ์šฐ์„  ์ œ๊ฐ€ JS/TS ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. TypeScript์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์€ ๊ฐ•๋ ฅํ•˜๋ฉด์„œ๋„ ๋ณดํŽธ์ ์ด๋ผ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ด๋ฏธ ์ต์ˆ™ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  GitHub Actions์˜ YAML ๊ตฌ์กฐ๊ฐ€ ๋ณธ์งˆ์ ์œผ๋กœ JSON๊ณผ ์œ ์‚ฌํ•˜๋ฏ€๋กœ, TS/JS์—์„œ JSON-like ๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋‹จ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์€ ๋ชจ๋“  gaji ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ์ด ๊ทธ ์ž์ฒด๋กœ ์œ ํšจํ•œ TS ํŒŒ์ผ์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. Deno์ฒ˜๋Ÿผ TS๋ฅผ ๋ฐ”๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์—์„œ gaji๋กœ ์ž‘์„ฑ๋œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ํ•ด๋‹น ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ JSON์œผ๋กœ ํ‘œํ˜„ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

์™œ action.yml ์ž๋™ ์ฝ”๋“œ์  ์ธ๊ฐ€? Client DevOps Team์—์„œ ์ปค์Šคํ…€ ์•ก์…˜์„ ์ž‘์„ฑํ•˜๋Š” ์ผ์„ ํ–ˆ๊ณ , ์ด๋ฏธ ์ƒ๋‹นํžˆ ๋งŽ์€ ์ปค์Šคํ…€ ์•ก์…˜์ด ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์˜ ๋ฌธ์„œ๋ฅผ ์ผ์ผ์ด ๋ณด๋ฉฐ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ํž˜๋“ค์—ˆ๋˜ ๊ฒฝํ—˜์ด ์ง์ ‘์ ์ธ ๋™๊ธฐ์˜€์Šต๋‹ˆ๋‹ค. Hackers.pub์— ๊ธฐ์—ฌํ•˜๋ฉด์„œ GraphQL ์ž๋™ ์ฝ”๋“œ์  ์˜ ๊ฐœ๋…์„ ์ ‘ํ–ˆ๊ณ , ๊ฐ™์€ ์ ‘๊ทผ์„ GitHub Actions์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ๊ตฌ์กฐ

gaji ์›Œํฌํ”Œ๋กœ์šฐ๋Š” getAction() โ†’ Job โ†’ Workflow โ†’ .build()์˜ ํ๋ฆ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. gaji dev --watch๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ƒˆ ์•ก์…˜ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ž๋™์œผ๋กœ ํƒ€์ž…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

import { getAction, Job, Workflow } from "../generated/index.js";

const checkout = getAction("actions/checkout@v5");
const setupNode = getAction("actions/setup-node@v4");

const build = new Job("ubuntu-latest")
  .addStep(checkout({}))
  .addStep(setupNode({
    with: {
      "node-version": "20",
      cache: "npm",
    },
  }))
  .addStep({ run: "npm ci" })
  .addStep({ run: "npm test" });

const workflow = new Workflow({
  name: "CI",
  on: { push: { branches: ["main"] } },
}).addJob("build", build);

workflow.build("ci");

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ๋ชจ๋“  ์•ก์…˜ ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ž๋™์™„์„ฑ, ์ปดํŒŒ์ผ ์‹œ์  ํƒ€์ž… ์ฒดํฌ, action.yml ๋ฌธ์„œ์˜ IDE ํžŒํŠธ ํ‘œ์‹œ, ๊ธฐ๋ณธ๊ฐ’ ํ‘œ์‹œ๊ฐ€ ๋ชจ๋‘ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. CompositeJob์œผ๋กœ ๊ณตํ†ต ๋กœ์ง์„ ํด๋ž˜์Šค๋กœ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜, CallJob์œผ๋กœ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋„ TS ์ฝ”๋“œ์ƒ์—์„  ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€: gaji ์ž์ฒด์˜ ๋ฆด๋ฆฌ์ฆˆ CD

gaji๋Š” ๋ชจ๋“  ci/cd๋ฅผ gaji๋กœ ์ž‘์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ์ค‘์—์„œ ์ œ์ผ ๋ณต์žกํ•œ release.ts๋Š” 4๊ฐœ์˜ Job์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • build: 5๊ฐœ ํ”Œ๋žซํผ(linux-x64, linux-arm64, darwin-x64, darwin-arm64, win32-x64) ํฌ๋กœ์Šค ๋นŒ๋“œ
  • upload-release-assets: GitHub Release์— ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ ์ฒดํฌ์„ฌ ์—…๋กœ๋“œ
  • publish-npm: npm์— ํ”Œ๋žซํผ๋ณ„ ํŒจํ‚ค์ง€ ํผ๋ธ”๋ฆฌ์‹œ
  • publish-crates: crates.io์— OIDC ๊ธฐ๋ฐ˜ ํผ๋ธ”๋ฆฌ์‹œ

์ด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ YAML๋กœ ์ปดํŒŒ์ผ๋˜๋ฉด ์•ฝ 180์ค„์˜ ํ‰ํƒ„ํ•œ ๊ตฌ์กฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ฃผ์„ ์—†์ด๋Š” Job ๊ฐ„ ๊ฒฝ๊ณ„๋‚˜ ์˜์กด๊ด€๊ณ„๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. TypeScript ๋ฒ„์ „์—์„œ๋Š” build, uploadReleaseAssets, publishNpm, publishCrates๋ผ๋Š” ๋ณ€์ˆ˜๋ช…๋งŒ์œผ๋กœ ๊ตฌ์กฐ๊ฐ€ ์ฆ‰์‹œ ํŒŒ์•…๋ฉ๋‹ˆ๋‹ค. 6๊ฐœ์˜ ์™ธ๋ถ€ ์•ก์…˜์„ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ , ๋ณต์žกํ•œ ๋งคํŠธ๋ฆญ์Šค ๋นŒ๋“œ์™€ OS๋ณ„ ๋ถ„๊ธฐ๊ฐ€ ์ฝ”๋“œ ๊ตฌ์กฐ ์•ˆ์—์„œ ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

gaji์˜ ํ•œ๊ณ„

gaji์—๋Š” ์—ฌ์ „ํžˆ ํ•œ๊ณ„๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๊ทผ๋ณธ์ ์œผ๋กœ, ์ตœ์ข… ์‚ฐ์ถœ๋ฌผ์ด ์—ฌ์ „ํžˆ YAML์ž…๋‹ˆ๋‹ค. GitHub Actions ํ”Œ๋žซํผ์˜ ์ž…๋ ฅ ํ˜•์‹์ด YAML์ธ ์ด์ƒ, gaji๋„ ๊ทธ ์ œ์•ฝ ์•ˆ์— ๊ฐ‡ํž™๋‹ˆ๋‹ค. gaji๋Š” ์ด ํ”Œ๋žซํผ ์œ„์—์„œ์˜ ์ตœ์„ ์ด์ง€, ์ด์ƒ์ ์ธ ํ•ด๋‹ต์€ ์•„๋‹™๋‹ˆ๋‹ค. ์›๋ณธ action.yml์˜ inputs๊ฐ€ ๋ฌธ์ž์—ด์ด๋‚˜ ์ˆซ์ž ์ •๋„๋กœ๋งŒ ํ‘œํ˜„๋˜๊ธฐ ๋•Œ๋ฌธ์—, "npm" | "yarn" | "pnpm" ๊ฐ™์€ ์„ธ๋ฐ€ํ•œ ๊ฐ’ ์ˆ˜์ค€์˜ ํƒ€์ž…๊นŒ์ง€๋Š” ์ œ๊ณตํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ${{ matrix.target.rust_target }} ๊ฐ™์€ GitHub Actions ํ‘œํ˜„์‹๋„ ์—ฌ์ „ํžˆ ์ˆœ์ˆ˜ ๋ฌธ์ž์—ด์ด๋ผ ํƒ€์ž… ๊ฒ€์ฆ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์ˆ ์ ์ธ ์ œํ•œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. gaji dev๋Š” getAction()์„ ์ •์  ๋ถ„์„ํ•ด์„œ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด๋งŒ ์ง€์›ํ•˜๊ณ , ๋ณ€์ˆ˜๋‚˜ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด ๊ฐ’ ์ž์ฒด์˜ ์˜คํƒ€(cache: "npn" vs cache: "npm")๋„ ์žก์„ ์ˆ˜ ์—†๊ณ ์š”.

์•ž์œผ๋กœ์˜ ๋ฐฉํ–ฅ

gaji์˜ ํ˜„์žฌ ์•„ํ‚คํ…์ฒ˜๋Š” TypeScript โ†’ Parse (oxc) โ†’ Execute (QuickJS) โ†’ YAML์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ์  ์„ ๋งŒ๋“ค๋ฉด์„œ ํ•œ ๊ฐ€์ง€ ์•„์ด๋””์–ด๋ฅผ ์–ป์—ˆ๋Š”๋ฐ, ํ”„๋ก ํŠธ์—”๋“œ(์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ์–ธ์–ด)์™€ ๋ฐฑ์—”๋“œ(YAML ์ƒ์„ฑ)๋ฅผ ์ž˜ ๋ถ„๋ฆฌํ•˜๊ณ , ์ค‘๊ฐ„ ์–ธ์–ด๋ฅผ ์ž˜ ์ •์˜ํ•˜๋ฉด TypeScript ์™ธ์˜ ๋‹ค๋ฅธ ์–ธ์–ด๋กœ๋„ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

1.0์—์„œ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ์„ ๋„์ž…ํ•ด ๋‹ค๋ฅธ ์–ธ์–ด ์ง€์›์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ๊ณ„ํš์ž…๋‹ˆ๋‹ค. gaji์˜ ํ•ต์‹ฌ ๊ฐ€์น˜์ธ action.yml ์ž๋™ ํƒ€์ž… ์ƒ์„ฑ์„ TypeScript์— ๊ตญํ•œํ•˜์ง€ ์•Š๊ณ  ํ™•์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

Special Thanks

gaji ๋ธŒ๋žœ๋“œ

์ด๋ฆ„ ์ œ์•ˆ์„ ํ•ด ์ฃผ์‹  kiwiyou ๋‹˜, RanolP ๋‹˜, ๋กœ๊ณ  ์ œ์ž‘์„ ํ•ด ์ฃผ์‹  sij411 ๋‹˜๊ป˜ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. Client DevOps Team์—๊ฒŒ๋„ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒ€์—์„œ ๊ฒช์€ ๊ฒฝํ—˜์ด ์•„๋‹ˆ์—ˆ์œผ๋ฉด YAML๊ณผ GitHub Actions์— ๋Œ€ํ•ด ์ƒ๊ฐํ•ด ๋ณด์ง€ ์•Š์•˜์„ ๊ฒ๋‹ˆ๋‹ค. emmanuelnk/github-actions-workflow-ts์—๊ฒŒ๋„ ๊ฐ์‚ฌ๋ฅผ ํ‘œํ•ฉ๋‹ˆ๋‹ค. TS๋กœ GitHub Actions๋ฅผ ํ‘œ๊ธฐํ•œ๋‹ค๋Š” ์•„์ด๋””์–ด์™€ ๊ธฐ๋ณธ์ ์ธ TS API ์„ค๊ณ„๋Š” ์—ฌ๊ธฐ์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

Read more โ†’
2
0
0
0
1

Hola! Llevo un tiempo en estas redes, en otros servidores pero creo que ha llegado el momento de sacarme de la manga una oficial!

Que me gusta? Plantitas! Me encanta aprender a cuidarlas. Como diria un personaje, me llena de orgullo y satisfacciรณn tener la casa llena de verde y vida.

Gatos! Siempre me considerรฉ de perros, realmente, pero todo cambiรณ cuando adoptรฉ a mis dos gatos. Ahora soy una loca de los gatos.

Me gusta vivir tranquilo, comer bien y cocinar, los videojuegos y la tecnologรญa (aunque no la nueva, el tema distรณpico se nos estรก yendo de manos).

Gracias por acogerme! :blobcatsign:

0
0

Street Art Utopia shared the below article:

Breathtaking (10 Photos)

STREET ART UTOPIA @streetartutopia@streetartutopia.com

Every now and then, a mural comes along that doesn't just decorate a wallโ€”it transforms the entire street. From massive portraits that capture the human soul to surreal scenes that blend with the architecture, these 10 artworks are pure visual magic. More: Simply Breathtaking (8 Photos) ๐ŸงŠ 1. Geometric Flow โ€” By Peeta in Mannheim, Germany Peeta turns rigid architecture into fluid, geometric sculptures using incredible anamorphic techniques. This massive piece in Mannheim creates an [โ€ฆ]

Read more โ†’
0

์นด๋ฉ”๋ผ/๋ Œ์ฆˆ ์‚ฌ์šฉ์ž๋“ค์€ ํ•œ ๋ธŒ๋žœ๋“œ์— ๊ฝค ๋งŽ์€ ํˆฌ์ž๋ฅผ ํ•ด์„œ ์ž์‚ฐ๊ฐ€์น˜์ธ ์ค‘๊ณ ๊ฐ€์— ๋ฏผ๊ฐํ•œ๋ฐ๋‹ค, ๋ธŒ๋žœ๋“œ ์ •์ฑ…์ด ์ž๊ธฐ์˜ ์‚ฌ์ง„๊ด€/๋ฏธ๊ฐ๊ณผ๋„ ์—ฐ๊ฒฐ๋˜๋‹ค ๋ณด๋‹ˆ ์นญ์ฐฌ์—๋Š” ์›ƒ์ง€๋งŒ ๋น„ํŒ์—๋Š” ํ™” ๋‚ด๋Š” ์ด๋“ค์ด ๋งŽ์Šต๋‹ˆ๋‹ค. ํ•„์ˆ˜ํ’ˆ์ด ์•„๋‹ˆ๋‹ค๋ณด๋‹ˆ ๋”์šฑ๋” ๊ทธ๋ ‡์ฃ ...

1

Breathtaking (10 Photos)

STREET ART UTOPIA @streetartutopia@streetartutopia.com

Every now and then, a mural comes along that doesn't just decorate a wallโ€”it transforms the entire street. From massive portraits that capture the human soul to surreal scenes that blend with the architecture, these 10 artworks are pure visual magic. More: Simply Breathtaking (8 Photos) ๐ŸงŠ 1. Geometric Flow โ€” By Peeta in Mannheim, Germany Peeta turns rigid architecture into fluid, geometric sculptures using incredible anamorphic techniques. This massive piece in Mannheim creates an [โ€ฆ]

Read more โ†’
0
0
0
0
0

์ผ๋ณธ์ธ๋“ค๋„ ์นด๋ฉ”๋ผ ๋ธŒ๋žœ๋“œ ๋ฐฐํ‹€์—์„œ๋Š” ์˜จ๊ฐ– ๊ณต๊ฒฉ์„ ๋‚จ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ใ…‹ใ…‹ใ…‹ใ…‹ "๋„ˆ ๋ญฃ๋„ ๋ชจ๋ฅด๋ฉด์„œ ํ‹€๋ฆฐ ์†Œ๋ฆฌ ์ข€ ์ž‘์ž‘ํ•ด๋ผ."๋ฅผ ๋Œ๋ ค ๋Œ๋ ค ๋งํ•˜๋Š” ์—ฌ๋Ÿฌ ์Šคํ‚ฌ์„ ๋ณผ ์ˆ˜ ์žˆ์ฃ . ๐Ÿ˜…

1
0