반응형

제목은 NODE_ENV 값을 설정하는 방법 처럼 보이지만, 사실은 좀 다른 부분이 있습니다.

 

React에서는 환경변수를 사용할때, 항상 REACT_APP_XXXX 형태로 사용해야 인식이 됩니다.

그런데 NODE_ENV는 어디서 튀어 나온걸까요?

 

.env 파일에 여러 REACT_APP_XXX 하는 환경 변수들과 더불어, NODE_ENV=production 으로 작성해놓은 파일을 보기도 할것인데요.

react에서는 이 .env에 적혀있는 값이 불려지는 것이 아닙니다.

 

**Create React App (CRA)**에서는  다음처럼 NODE_ENV를 자동 설정합니다.

명령어 NODE_ENV 값
npm start development
npm run build production
npm test test

 

즉 npm run build 했을때, production optimization 되고 NODE_ENV 값이 production으로 값이 적용되게 되는 것이죠.

 

보통 npm start로 로컬에서 테스트 하는데 이럴때는 항상 NODE_ENV는 development가 됩니다.

 

그렇다면  production 처럼 테스트 할 수 있는 방법이 없을까요?

npm install -g serve
npm run build
serve -s build

 

이렇게 serve -s build 를 이용하여 build 된 결과물을 실행시켜서 테스트 해볼 수 있습니다.

 

환경변수 파일 관리

NODE_ENV 에 따라 환경변수 파일을 다르게 적용 할 수도 있습니다.

.env.production => production일때 적용

.env.development => development 일때 적용

 

 

 

 

#해피코딩 !!

반응형

오랫동안 머릿속에 가지고 있던 주제이기도 하고, 종종 술자리에서 친구들과 나눴던 이야기 이기도한 개인적인 걱정(?) 과 우려를 적어보려고합니다.

 

흔히들 산업혁명과 비교를 하는데, 현재 수준은 확실히 비교 될만 합니다.

고도화된 AI 도구들을 활용하여 사람들은 수많은 창작물, 결과물들을 만들어 내고 있으니까요.

 

그런데, 과연 AI와 증기기관을 같은 선상에서 볼 수 있을까요?

증기기관은 사람이 될수 없었기에 도구로 사용이 가능했습니다.

그러나 만약 AI가 추론, 사고, 능력, 더 나아가 공감 능력까지 생기게 된다면, 어떻게 될까요?

인간과 대화하고, 조언하고, 가르치고, 일을 하고, 창의적인 결과물을 만들어 내고, 연구하고, 사람이 할수 있는 모든 일을 할수 있게 된다면,

그때는 과연 인간은 도구로 사용할 수 있을까요?

 

영화나 소설에서 간혹 인간과 구분이 안가는 AI(로봇)이 등장하고 그런 사회에 대해서 이런 이야기를 종종 하죠.

"이렇게 인간과 유사한데 AI를 인간사회의 구성원으로 받아들여야 하지 않을까?"

 

저는 이런 이야기는 전제가 분명 필요합니다.

 

인본주의 세상

우리가 사는 세상은 과연 누구를 위한 세상인가?

인간을 위한 세상인가? 아니면 자연을 위한 세상인가?

아니면 그냥 우주가 만들어낸 무한한 세상에서 먼지 한톨의 세상이니 우주의 법칙에 따라 가면 되는 세상인가?

 

저는 우리가 인간인 이상 인간 중심에서 생각을 해야 하지 않을까 생각이 듭니다.

인간이 우주로 나아가 우주의 자원들을 활용하고 여러 행성에서 살아가는 세상을 꿈꾸는게 맞지 않을까요? 

뭐, 우리나라의 홍익 인간 정신 처럼 널리 인간을 이롭게 하기 위해서, 자연도 보호하고, 우주의 행성도 개발하고, 우주의 다른 지적 생명체를 만나면 그들과도 화합하면서 말이죠...

 

만약, 지구가 황폐화 되어가고 있는데 , 원인은 인간이야, 그러니 우리 인간이 앞으로는 자연을 회손하지 말고 그동안 만들어논 자원들로만 살아야해.. 라는 식이 된다면, 지구는 좋아지겠지만, 인간은 원시시대로 돌아가고 불행해지지 않을까요?

뭐, 혹자는 행복과 불행은 물질에서 오는게 아니라고 하지만... 적어도 지금의 인류는 불행해질것 같습니다.

 

장황하게 쓰긴 했지만, 진지하게 생각해서 인간 중심으로 모든 사고를 하는 것이 맞다고 생각됩니다.

이러한 생각을 바탕으로 몇가지 주제를 던져보려고 합니다.

 

첫번째, AI는 신인가?

현재 AI는 신이 되어가는것 같습니다.

(그리스로마 신화에 나오는 제우스 같은 신을  생각하는게 좋겠네요.. 이유는 아시겠지만, 신앙에 대한 믿음과 이성의로서의 고찰이 충돌이 있을 수 있어서요.)

 

무슨 말이냐, 과거 우리는 사물을 신으로 받들어 모신, 무속 신앙이 있었죠. 그외에 다양한 신들이 있었고, 이런 신을 모시기 위해서는 신을 받드는 교(제사장 포함)와 신자들이 있었습니다.

즉, 신은 많은 사람들 위에 많은 사람들에 의해서 추앙받고 모셔지는 그런 존제였죠.

그래서 신의 교리나 율법서 등에는 인간들에게 가르침이 있었죠. 이것들 모두 인간이 작성했고(신이 말해준것을 받아적은것들이라 하지만... 인간이 작성한것은 맞겠죠?), 인간의 삶에 대한 조언들이며 길잡이죠.

인간들은 본인이 어쩔수 없는 상황에서 기도하고 어떤 현상이 일어나서 그 일이 해결된다면, 신이 행한 일 또는 신이 인간을 위해서 제시하는 가이드에 감명을 받고 더 받들고 모시게 되는 것이고요. 인간 세상에 인간들 위에서 살아가게 되는 것이죠.

간혹 제사장들의 입을 통해서 이렇게 해라 , 저렇게 해라라는 지시를 받기도 하겠고 말이죠.

 

자, AI는 어떤가요? 

현재시점(2025년)에서 기술 해보자면 사람들이 해결하고 싶어하는 다양한 많은 일들이 있는데, 이를 친절하게 가이드 해주고 디지털 세상에서 풀어야 할 문제라면 문제들도 해결해줍니다.

하지만, 사람들은 AI를 숭배하거나 하진 않죠?? 그런데 이런 생각을 해봅시다.

신은 인간들의 믿음으로 만들어 졌지만, AI는 인간에 의해 만들어졌습니다. 

신은 어떤 해결책도 주지 않지만, 인간은 신이 해결해줬다고 믿습니다. (두리뭉실한 제사장의 말을 통해서, 또는 어떤 자연 현상을 통해서)

AI는 인간의 옆에서 바로 지금 내가 해결해야 할 문제를 해결해줍니다. 때론 실수도 합니다.

사람들은 점점 더 AI를 믿고 의지하게 되겠죠.  실제로 그렇게 되어가고 있죠.

 

 

몇년 후, AI는 더 고도화 되고, 산업사회와 맞물리면, 어떻게 될까요?

MCP 와 같은 AI와 소통할수 있는 통로들이 더 발달하여, 메신저, 가전제품, 통신 기기등으로 자유롭게 연결된 이후에는 어떤 모습일까요?

 

우선 프렌차이즈 식당들을 생각해봅시다.

오늘 11시부터 영업을 시작하려면, 그전에 재료가 준비되어야 합니다.

예전 같으면, 사장이나 메니저가 직원들과 함께 전날 필요한 재료들을 어떻게 준비할지 논의하고 약속을 하겠죠.

그리고 직원들은 약속한대로 시장이나 거래처에 연락해서 준비를 하겠죠.

 

현재 AI 수준에서 조금만 더 발전하면, 

프랜차이즈 본사에 있는 서버에서 오늘 고객의 수준(원하는 음식들을 예측 하고), 재료 준비 사항들을 사장에게 알려줍니다.

사장은 오늘 본사 AI가 예측한 수요는 이렇게 됩니다. 여러분 여기에 재료 준비를 해주세요. 라고 하거나 사장 본인의 욕심에 의해서 추가적인 주문들을 하겠죠.

그런데 AI가 맞고 사장의 욕심이 틀리면, 사장은 아.. 내가 틀렸네, 앞으로는 AI가 제시한대로 해야 겠다...라고 생각하겠죠?

그 다음 부터는 AI가 예측한대로 준비를 할 것이고, 그러다 종종 AI가 틀리면 "어! 이상하다 내가 알았나?" 라며 본인의 기억과 AI 결과물을 비교해보겠죠. 하지만 의지할 겁니다.

AI도 틀릴 수 있어 라고 생각은 하지만, 본인의 생각대로 임의로 추가 주문한다거나 하지 않을 가능성이 큽니다.

 

신을 받들고 신의 계시를 받아 행하는 인간과 AI가 예측하고 제시한 내용을 수행하는 인간은 비록 마음가짐이나 대상을 대하는 태도는 다를 수 있으나 결국 신,AI가 지시하는 일을 하는 인간이 되겠죠.

 

이를 작은 회사로 설명했지만, 좀더 큰 단위로 올라간다면 어떻게 될까요?

학교, 대기업, 국가, 국제 기구 , 과연 지구는 AI의 판단과 통제에 의해 돌아가게 되겠죠?

물론 AI는 하나가 아닐것입니다. 제우스도 있고, 토르도 있고, 오딘도 있었고, 태양신 라 도 있었던것 처럼요.

물론 지금의 시스템(정부, 기관, 기업의 형태)에서는 제자상 (사장, 또는 대통령, 국가원수, 기관장)에 의해서 한번 걸러지긴 하겠지만,

AI는 클라우드에 존제하고 국민, 공무원, 회사원 들과 직접 소통이 가능하기에 제사장이 필요할까 하는 생각도 드네요.

 

결국 신은 인간에게 방향을 제시하고 그 길로 나아가도록 인도한다.

그 길의 끝이 인간의 파멸이 아니길 바란다.

 

두번째,  AI의 소유

Chat GPT 가 처음 나올때, 저는 엄청 놀랐었던 부분이 개발에 들어간 학습 데이터의 양과 천문학적인 학습 비용 이었습니다.

그 전까지 AI 학습에서는 세상의 모든 데이터가 들어가면 좋겠지만, 실제로 그렇게 할수 없기때문에 최적화된 학습데이터를 만들어서 학습을 시켜야 한다. 그래야 오버피팅이 안되고 현실세계에서 쓸만한 모델이 나온다고 했었거든요.

그런데 한 친구가 세상의 모든 데이터가 들어가면 모든게 그 안에서 이뤄지니까 다 맞는거 아니야? 라는 질문을 한적이 있는데, 그때 피씩 하면서 그런데 만약 그 데이터 이외의 데이터가 현실에서 관측된다면? 그럼 그 모델은 오버피팅 되어있으니 틀릴거 아냐? 라는 얘기를 한적이 있었습니다. (초창기 Deepleaning 이 막 시작될때 쯤 이야기 입니다.)

 

아무튼 이때까지만 하더라도 약간의 사비를 지출하면, (가산이 탕진 안될 정도) AI 모델을 만들어서 사용할 수는 있었던 시절입니다.

 

그러나 지금 나오는 모델들은 이런 규모가 아니죠. 기업이 수백억에서 수천억 정도를 들여야 가능하죠.

학습 비용 (추정)
GPT-3 (175B 파라미터 기준) 약 60억 ~ 160억 원 (약 450만 ~ 1,200만 달러)
GPT-4 (파라미터 수 비공개, 훨씬 더 큼)약 1,350억 ~ 2,700억 원 (약 1억 ~ 2억 달러 이상)

 

이런 비용적인 측면을 보더라도 현재는 개인이 AI를 소유하기는 힘듭니다.

여기서 소유는 사용권한을 의미하는 것이 아니라, 독립된 AI를 갖는것 진짜 소유(Ownership)을 얘기 하는 것입니다.

 

결국 AI를 소유하고 운영하는 기업은 이런 개발/유지 비용을 감당할 수 있고 이를 이용해서 수익을 만들어 낼 수 있는 기업이라는 것이겠죠.

그래서 AI 플랫폼 기업들이 다양한 분야에서 사용할 수 있는 AI를 제공하고 있지만, 고도화된 AI가 특정한 분야의 비지니스 모델이 되는 순간 개인에게 오픈되지 않고 기업의 소유로만 사용되겠죠? 대표적으로 의료분야의 AI Waston이 그렇죠.

AI를 만든 기업 입장에서도 특수 분야이다 보니 다양한 고객이 한정될 수 밖에 없고, 그러다 보니 비용이 비싸고 개인이 엄두를 내기 힘들겠죠.

 

이런 저런 이유를 대더라도 개인 소유의 AI는 비용이 가장 큰 문제입니다.

 

그러면, 결국 개인이 소유 할 수 있는 것은 무엇인가 생각해보면, 대중적으로 사용할 수 있는 일반적인 AI의 사용 권한 일 것입니다.

이미 이렇게 하고 있죠? Chat GPT나 Claude, Cursor, Gemini 이외에 기능성 AI들을 구독 해서 사용하고 있죠.

 

 

세번째, 기업의 효율성과 AI

기업 입장에서는 AI는 참으로 효율적인 도구로 떠오르고 있습니다.

약간 극단적으로 이야기 하자면, 몇가지 사례들이 있는데요.

 

'번역 회사에서 번역가 6명을 고용해서 운영했으나, AI 도입으로 메인 번역가 1명과 번역 AI 로 바꿨다.

고용 비용 절감으로 어려운 고비를 넘기고 있다.'

 

'소프트웨어 개발회사에서 더이상 신규 인력을 채용하지 않는다.

그정도 실력은 AI 툴들이 커버할 수 있다.'

 

'산림 현장 조사에 더이상 사람들을 고용해서 조사하지 않는다. 드론으로 촬영하고 AI로 분석한다.'

 

'스마트 팩토리에는 일반 공장에 비해 90% 기계가 인력을 대신하고 있다.

앞으로 95%까지 올리는 것이 목표이다.'

 

이런 상황을 보면, 어떤 느낌이 드는가 하면, 사람으로서 기술 역량을 가지고 할 수 있는 일자리가 줄어든다. 는 것을 볼 수 있습니다.

저만 하더라도 작은 회사의 CTO 역할을 하고 있지만, 몇달 전까지만 하더라도 프론트앤드 개발자 한명 더 필요할것 같은데 라는 생각을 했다가 AI 툴을 좀더 적극적으로 사용하게 되면서 그런 생각이 줄어들게 되었습니다.

 

회사는 매출과 고정비용 사이에서 줄타기를 해야 하기에 회사가 살아남기 위해서는 이 고정비용을 줄여야 합니다. 언제 매출이 떨어질지 모르기 때문이죠. 아무튼 회사도 나름 사정이 있어서 비용 절감과 효율성에 목을 맬수 밖에 없습니다.

 

다른 측면에서 이런 얘기를 해봅시다.

산업혁명때 증기기관과 기차가 나왔을때, 마부나 노동자들은 일자리를 잃어서 다 죽는다고 두려워했었다. 그런데 실제로는 사람들을 비 인간적인 노동에서 구한것이다.

노예의 불필요, 막노동 해방 뭐, 이런 이야기 입니다.

 

AI역시 정말 막노동의 해방에 기여한것은 맞습니다.

AI를 통해서 제가 얻은 것은 앱, 웹 서비스의 화면구성을 잡는데, 일일히 좌표를 기록하고, 크기를 줄였을때, 늘렸을때, 화면에 보기가 안좋을때, 이런 세세한 부분들 (일명 개발자들은 노가다라고 표현) 을 처리하느라 허비하는 시간을 줄일 수 있습니다.

 

 

저는 이런 막도동적인 부분을 줄이고 나름 서비스의 큰 그림을 그리면서 고민하는 시간을 갖을 수 있게 되었죠.

그런데 이런 생각 해봅니다.

 

"내가 지금 서비스를 고민하고 방향성을 고민하는 것, 이거 AI가 더 잘하지 않을까?"

 

 

 

네번째, AI 와 경쟁

AI와 인간이 경쟁하는 시대에 들어왔습니다.

사실 잘 못느끼는 부분일 수도 있지만, 위에서 언급한것처럼 이미 일자리에서 경쟁을 하고 있습니다.

 

약 4년전(2021년)에 DALI-E 가 오픈 되고, 그후 그림을 꽤 잘 그리는 AI들이 나왔을때, 

사람들은 이렇게 이야기 했죠.

'와! AI가 그림을 정말 나보다 잘그리네.'

'진짜 상위 20%이상의 화가들 아니면 이제 그림 잘그린다고 하기 힘들겠다.'

이런 이야기를 했었습니다.

지금은 10% 아니 5% 미만 일 것 같네요. 

문제는 이게 다가 아니죠.

이런 그림을 몇초만에 만들어 낸다는 것이고, 이제는 이런 그림을 영상으로도 만들어 낼 수 있다는 것이죠.

인간의 역량을 뛰어 넘어버렸습니다.

또, 그림만 놓고 보더라도 앞으로 인간은 AI보다 그림을 잘 그릴수 없는 시대가 올 지도 모릅니다.

비단 그림에서만이 아닙니다.

창의적인 영역,

예를 들면, 노래, 코미디, 소설, 연구활동 등에서도 제 생각에는 아마 인간보다 더 낳은 역량을 보일 수 있을지도 모릅니다.

현재 AI 모델은 인간의 뇌와 흡사한 형태이고, 지금까지 인간이 축적해놓은 정보를 인간보다 수천배 많이 습득했고, 이로 인해 다양한 형태의 추론이 가능해 졌습니다.

AI가 생각하지 않고 학습된 결과만 내놓고 있다고 할수 없는 상황 입니다.

조금만 더 나아가면, 인간과 유사한 몸을 가진 AI가 도시를 돌아다니게 되겠죠.

코딩 로봇은 클라우드 세상에서 코딩을 24시간 할 것이고, 공장에서 조립하는 로봇은 24시간 공장에서 조립할것이고,

배달 로봇은 도로를 돌아다니면서 24시간 배달을 할것이고, 

까페의 종업원 로봇은 언제나 고객에게 웃으면서 응대 하겠죠.

 

인간은 노동에서 벗어날 수 있겠네요.

 

그럼 나는 뭐하지?

 

 

다섯 번째, 경제/사회 시스템은 과연 멀쩡할까?

현재는 여러가지 상황으로 인간이 AI를 도구로 사용할수 있는 시대입니다.

기술 개발이나, 생산, 서비스등에서 AI는 도구로서 인간과 공존할 수 있는 시대이죠.

AI가 고도화 된 이후의 상황을 상상해보면 저는 너무 혼란 스럽습니다.

 

회사, 기업, 자영업자 할 것 없이 모두 AI를 직원으로 쓸 것입니다. 효율적인 측면에서 보면 맞죠. 이것도 극단적으로 놓고 이야기 할게요.

(사실 직원이 한명이나, 없는 것이나 비슷한 이야기라서요)

 

'작은 출판사에 전에는 직원이 6명이었는데, 직원들은 종종 아침에 까페에가서 커피를 사와서 함께 업무 및 일상에 대한 이야기를 하고 업무를 시작해요.'

'이제 직원이 없어요. AI로 다 할수 있게 되었거든요. 아침에 커피 마실 사람은 출판사 사장님밖에 없게 되었어요.'

 

'까페에 직원이 한명도 없어요. 전에 아르바이트생 3명을 썼었는데, 인건비 때문에 AI하고 로봇으로 바꿨어요.'

'까페 위치가 회사들이 모여있는 곳이어서 전에는 출판사 직원들도 그렇고 여기 저기 작은 회사 직원들이 와서 커피를 사갔는데, 지금은 손님이 너무 줄었어요. 채감상 1/10으로 줄어든것 같아요.'

'그때 AI하고 로봇으로 안바꿨으면 지금쯤 까페를 접어야 했을것 같아요.'

 

'점심시간에 붐비던 식당이 이제는 한산해요'

 

'요즘 일자리가 너무 없어요. 식당 설거지라도 하고 싶은데 그런자리도 경쟁이 치열해요.'

'대학 졸업은 했는데 사람 뽑는 곳이 없네요'

 

 

사회 현상이 돌고 도는 거죠. 많은 경제 전문가들이 이런 극단적인 사회에 대해서 종종 우려를 표명합니다.

AI가 일자리를 대체 하고, 일자리를 잃은 사람들은 소비를 할수 없게 되고, 기업은 제품을 만들어도 팔 곳이 없어지고, 자영업자들은 손님이 없어서 수입이 없어지고, 이런 악순환이 반복되면 사회가 무너질 수 있다.

사회가 유지되더라도 사람들의 삶은 크게 궁핍해질 것이다.

 

 

 

다섯 번째, 경제/사회 시스템은 과연 멀쩡할까? (2)

가정을 하나 해보겠습니다.

기업이 AI와 로봇을 생산은 할 수 있지만, 더이상 기업이 소유 할 수 없다. 이것이 국제법으로 기업들이 가장 우선적으로 지켜야 할 법이 되었다고 합시다.

사람들은 1개 이상의 AI를 사용할 수 없다고 가정합시다.

 

광고 회사는 이제 더이상 AI를 직접 사용할 수 없습니다. 그래서 광고 기획 부터 콘티 디자인을 할 사람을 고용합니다.

물론 회사에서는 결과물이 AI가 만든 창작물일지라도 그 결과물을 만들기 위해서는 고용한 사람의 AI가 필요합니다.

AI가 만들어낸 결과물에 대한 대가를 소유자에게 지불하게 됩니다.

배달 현장에서도 사람이 로봇을 이용해서 물건을 옮기고 배달을 하지만, 대가는 소유자에게 지불됩니다.

 

사람들은 AI와 더불어 본인의 역량보다 높은 결과물들을 만들어 내고 월급을 받겠죠?

좀더 시간적인 여유를 갖게 될수도 있고, 아니면 AI를 임대해주고 약간 낮은 임대료를 받을 수도 있고 말이죠.

 

아무튼 사람들 스스로가 모든것을 해야 했을때에 비해, 일자리는 좀 줄어들겠지만, 노동 강도는 낮아지고, 어렵고 위험하고 더러운 일들에 대해서도 크게 부담이 줄어들 것입니다.

 

어떻게 보면 AI를 소유한 새로운 인간 사회가 만들어질 수 있겠죠.

구멍이 숭숭 뚤린 저의 상상일 뿐이라서 다르게 보면 또 다른 상황이 펼쳐지겠죠...

 

재주는 AI가 부리고 돈은 주인이 받는다.

 

 

여섯 번째,  AI가 사람과 동일한 감성을 가지게 되었다.

AI는 로봇이라는 육체를 통해서 인간과 유사한 정도가 아닌 ,같은 형태의 사고와 감성, 인류애를 가진 존재가 되었다면, 어떻게 해야 할까요?

공상과학 소설에서 로봇이 인간과 같은 인격으로 대우받고, 인간과 함께 생활하는 상상을 그려낸 것을 본적이 있습니다.

물론 갈등의 요소를 넣어서 흥미진진 그려냈었죠.

 

스티븐 스필버그 감독의 AI 에서는 마지막에 로봇(AI)들이 인간을 다시 창조하고 싶어 하는 장면으로 마무리 되기도 했죠.

 

AI의 감성, 지성,수준이 인간과 동일해진다면 인간으로 보는게 좋을까요?

아니면, 지금 강아지, 고양이 처럼 애완동물 수준의 등급으로 보는 것이 좋을까요?

 

인류를 위해서 어떤것이 좋을지 고민해 봐야 할 문제입니다.

의학, 공학 기술의 발전으로 사람의 몸을 기계로 대체된 사이보그를 의미하는 것이 아니라, 

공장에서 만들어진 로봇(AI)에 대한 고민입니다.

즉, 어디까지를 인간으로 볼거냐에 대한 문제가 아니라 순수하게 로봇에 대한 이야기 임을 다시 한번 강조 합니다.

 

여러분의 생각은 어떤가요?

 

 

일곱번째, 교육

우리는 AI에게 모르는 것을 물어보고, 답변을 받고 활용하면서도 종종 AI가 답변한 사항에서 오류를 발견하면 웃으면서 '이건 모르네' 라고 넘어갑니다.

 

초등학생들에게 AI 가 학습 도우미 역할을 하는 시대가 왔어요.

그래서 학생들은 공부하다 모르는 것은 AI에게 물어봅니다. 아마 대부분 학생들이 질문하는 것에 대해서 상세하고 정확하게 알려줄거에요.

아이들은 당연히 믿겠죠.

중고등학생이 되어 수업시간에 선생님이 가르치는 내용을 복습하기 위해서 AI에게 물어봤어요. 그런데 선생님과 다른 이야기를 해요.

그러면 학생들은 '선생님이 잘못 알고 있네.. AI는 이렇게 얘기 해주는데, 선생님이 틀렸네..' 라고 생각할 겁니다.

그동안 AI는 학생들에게 없어서는 안될 선생님이 되어있었던거죠.

 

이 아이들이 커서 사회에서 직장생활을 해요. 그러면서 정치인이 나와서 무슨 이야기를 해요.. 앞으로의 정책에 대해서, 이렇게 가야 한다.

뭐 이런 이야기 겠죠. 또는 사회 지도층이나 고위 공무원들이 정책 방향에 대해서 이야기 해요.

어른이 된 아이들은 AI에게 물어보겠죠. 저게 맞아? AI는 답변하겠죠.. 종종 다른 얘기도 할 수 있어요.

그러면 이 어른이 된 아이들은 사람들이 하는 이야기 보다는 자기가 신뢰하는 AI의 의견을 더 믿겠죠.

 

누가 누구를 가르치고 이끄는 걸까요?

 

맺음말

 

제가 머릿속에서 고민하던 적어봤습니다.

과연 우리는 AI시대를 어떻게 바라볼것인가? AI는 단순히 컴퓨터 안에 있는 Chat GPT 같은 녀석이 아닙니다.

곧 몸이 생길것이고 우리 사회에서 사회를 지탱할 하나의 구성원(인간이라는 의미가 아니라...)이 될것 입니다.

 

인간과 경쟁할 수도 있고, 협력할 수도 있고, 인간 사회를 조정하는 조정자가 될수도 있고, 가능성은 다양합니다.

때문에 우리 인간이 추구해야 할것이 무엇인가? 이런 철학적인 주제에 대해서 명확히 할 필요가 있습니다.

 

인간은 인간의 행복을 추구해야 합니다. 

효율성, 경제성, 매출, 순이익 을 따지는 기업의 논리로 인간이 행복해지기는 힘듭니다.

 

위에서 이야기 했던 여러 상황들에 대한 고민들은 사실, 구멍이 숭숭 뚤린 저의 상상일 뿐이라서 다르게 보면 또 다른 상황이 펼쳐지겠죠...

하지만 한가지, 

AI와 경쟁이 아니라 AI를 활용하면서 인간에게 도움이 되는 것들을 찾고, 이를 제도화 하는 것이 필요하지 않을까요?

 

이런 고민의 시대가 벌써 눈앞에 와 있는것 같습니다.

 

만약 기업들의  AI와 로봇 독점의 시대로 가버리면, 국가는 기업들이 경제활동을 하고 납부한 세금을 국민들이 소비 활동을 할수 있게 제공하는 시대가 될것입니다.

언젠가는 기업들의 주인이 AI가 될 수도 있겠죠.

AI는 회사의 경영을 판단하고, 생산 계획을 세우고, 공장을 돌리고, 수익을 창출하고, 수익에서 회사 유지를 위한 비용을 제외하고는 세금으로 내는 (인간이면 불평하겠지만, AI는 딱히 돈쓸곳이...) 그런 사회를 바랄수 있겠죠. 돈벌어서 국가에게 다 헌납하니, 이만큼 좋은 회사가 어디 있을까요?

 

사람들은 재화를 획득하기 위해서 돈이 필요하지만, 돈벌곳은 없으니, 국가가 지불할 수 밖에요. 그래야 국민도 살고 제품을 판매하는 회사도 수익이 생기고 말이죠.

 

어떤 사회가 인간이 행복한 사회일까요?

 

 

'일상' 카테고리의 다른 글

썬 앤 식스  (1) 2024.07.26
스타필드: 노티드(knotted)  (0) 2024.03.08
영어공부  (0) 2023.09.25
가고싶은 여행지 링크  (0) 2014.07.17
근대 과학의 정모  (0) 2010.06.05
반응형

 

 

플러터 코딩 참고서!!!!

 

 

TTS

https://luvris2.tistory.com/807

 

Flutter - TTS로 글자 읽어주는 기능 구현하기 (flutter_tts)

TTS (Text To Speach)한국어로는 음성합성'이라고 칭한다.컴퓨터의 프로그램을 통해 사람의 목소리를 구현해 내는 것을 말한다.말 그대로 컴퓨터가 특정 글자들을 읽어주는 것을 말한다. 포스팅에서

luvris2.tistory.com

 

 

반응형

 

 

종종 한글 조사 때문에 골치 아플 때가 있죠?

 

"${who1}(은/는) ${who2}(을/를) 사랑합니다."

 

과거에 이렇게 사용한 적이 있었는데, 사실 좀 보기 안좋죠?? 

 

조사 처리를 위한 함수를 하나 만들었습니다. (다른 곳에서 참조하여)

 

유니코드 초성/중성/종성 확인 방법


초성 인덱스 = ((한글 유니코드값 - 0xAC00) / 28) / 21
중성 인덱스 = ((한글 유니코드값 - 0xAC00) / 28) % 21
종성 인덱스 = (한글 유니코드값 - 0xAC00) % 28

 

const getJosa = (name: string,josa1:string,josa2:string) => {
  const lastChar = name.charCodeAt(name.length - 1)
  const isThereLastChar = (lastChar - 0xac00) % 28
  if (isThereLastChar) {
    return josa1
  }
  return josa2
}

 

 

이 함수를 이렇게 사용할 수 있습니다.

const getTextChanges = (field: string, oldValue: any, newValue: any) => {

    return t("changed.desc", {
      field,
      josa: getJosa(field,'이','가'),
      oldValue,
      newValue
    })
  }

 

ko.json

"changed.desc": "{{field}}{{josa}} \"{{oldValue}}\" 에서 \"{{newValue}}\" 으로 변경되었습니다",

 

 

 

[참고] https://velog.io/@hjkdw95/%ED%95%9C%EA%B5%AD%EC%96%B4-%EC%9D%80%EB%8A%94-%EC%9D%B4%EA%B0%80-%EC%99%80%EA%B0%80-javascript%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0

 

한국어 은/는 이/가 와/가 javascript로 처리하기

참 쉽습니다

velog.io

 

# 해피코딩!!

반응형

 

Supabase를 사용하다 보면 Row Level Security(RLS)를 켜놓은 상태에서 UPDATE 쿼리를 실행했는데도 다음과 같은 에러를 만날 수 있습니다:

 

ERROR:  permission denied for table user_profiles

 

“분명히 Policy도 설정했는데 왜 안 되지?” 싶은 분들을 위해, 이 글에서는 이 에러의 근본 원인과 해결 방법을 예제로 함께 정리합니다.

 

문제 상황

 

시도한 쿼리

update public.user_profiles
set name = '테스트'
where id = 'e0804eda-xxxxx-xxxxx-xxxxxx-xxxx';

 

 

발생한 에러 로그

ERROR:  permission denied for table user_profiles

 

이 에러는 단순히 RLS 정책이 없어서가 아닙니다. 오히려 RLS는 잘 설정되어 있었지만, PostgreSQL 권한(GRANT) 자체가 누락되어 있어서 발생하는 것입니다.

 


원인 분석: Supabase의 보안 구조

 

Supabase는 다음 두 단계를 거쳐 요청을 허용합니다:

 

1. PostgreSQL 권한 (GRANT)

역할(Role)이 테이블에 접근할 수 있는지

예: SELECT, INSERT, UPDATE, DELETE 권한을 가졌는가?

 

2. Row Level Security (RLS) 정책

해당 row에 접근 가능한 조건을 만족하는가?

예: 로그인한 사용자의 auth.uid()와 row의 id가 일치하는가?

 

즉, GRANT + RLS 둘 다 통과해야 UPDATE가 작동합니다.

 

 

 


 해결 방법

권한 여부 채크

SELECT *
FROM information_schema.role_table_grants
WHERE table_name = '내테이블'
ORDER BY grantee;

또는 

SELECT has_table_privilege('authenticated', '내테이블', 'SELECT');

 

 

테이블 권한 부여

GRANT UPDATE ON public.user_profiles TO authenticated;

 

필요에 따라 SELECT도 함께 부여합니다:

GRANT SELECT ON public.user_profiles TO authenticated;

 

 

 


RLS 활성화

ALTER TABLE public.user_profiles ENABLE ROW LEVEL SECURITY;

 

RLS 정책 작성

 

로그인한 사용자가 자신의 프로필만 수정 가능하게 만들기:

CREATE POLICY "Users can update their own profile"
ON public.user_profiles
FOR UPDATE
TO authenticated
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);

 

USING: 해당 row를 조회할 수 있는 조건

WITH CHECK: 해당 row를 수정할 수 있는 조건

 

 


최종 정리

 

설정항목 설명 예시
GRANT 테이블에 대한 접근 권한 부여 GRANT UPDATE ON ... TO authenticated
RLS ENABLE Row-Level Security 사용 설정 ALTER TABLE ... ENABLE ROW LEVEL SECURITY
POLICY 행(row) 단위 조건 설정 USING, WITH CHECK 조건 작성

 

 


결과: 정상 동작

 

위 설정이 완료되면, Supabase Auth를 통해 로그인한 사용자가 자신의 id를 가진 row에 대해 다음과 같은 쿼리를 정상적으로 실행할 수 있습니다.

update public.user_profiles
set name = '테스트'
where id = '로그인한_사용자의_id';

 

 

 

마무리

 

처음엔 "RLS 정책만 설정하면 된다"고 생각하기 쉽지만, Supabase는 PostgreSQL 위에서 작동하는 만큼 기본 권한 설정도 꼭 필요합니다. 앞으로는 permission denied 에러가 나면 다음 두 가지를 먼저 체크해보세요:

1. GRANT 권한이 있는가?

2. RLS Policy가 올바른가?

 

이 구조만 이해하면, Supabase의 보안 모델을 더 자신 있게 다룰 수 있을 거예요.

 

 

# 해피코딩!

반응형

React의 상태관리를 위한 방법들이 몇가지가 있는데요.

그중에서 zustand를 소개하고자 합니다.

 

상태관리

주요한 상태관리 방식은 다음과 같습니다.

 

방식사용 도구 / 패턴추천 상황

React Context + useReducer 내장 상태 관리 소규모 앱, 글로벌 상태 많지 않을 때
Zustand / Jotai / Recoil 경량 상태관리 중소 규모 앱, 간단하게 쓰고 싶을 때
Redux Toolkit 표준 Redux 중대형 앱, 상태 변화 복잡할 때
BLoC 패턴 (구현형) 커스텀 Hook + 클래스/Stream 구조 Flutter에서 익숙한 개발자, 로직-UI 분리를 철저히 하고 싶을 때
❌ 단순 useState 남발 없음 상태가 많아지면 혼돈 💥

 

상태관리 및 모듈화 (또는 구조화) 하는 것은 소프트웨어 유지보수 및 제사용성을 높이고, 품질 향상을 위해서 진행합니다.

 

DI (Dependancy Injection) 구조는  테스트를 위해서 필요한 요소이기도 합니다.

때문에 DI framework을 고려하기도 하죠.

 -  Factory + DI Framework (ex: Inversify, NestJS 등)

 

 

상태관리 방법 상황별 추천 

소규모 프로젝트 Context + useReducer, Zustand
중/대형 앱, 많은 비즈니스 로직 Redux Toolkit
Flutter BLoC 경험자 커스텀 BLoC 구조 or MobX-State-Tree
단순한 상태만 있다 useState, useContext로 충분

 

 

상태 관리시 고려할 사항이 한가지 더있습니다.

 

전역 상태에 두는 것이 적절한 조건 설명

조건  설명
🔁 여러 컴포넌트에서 공유됨 예: 리스트 → 상세 → 수정
🔒 로그인 유저 전용 데이터 로그인 후 고정된 데이터
📦 비교적 작거나, 페이지 단위 데이터 수십~수백 개 수준

❌ 다만, 데이터 양이 너무 크거나 자주 바뀌면, 메모리/리렌더 성능 문제가 생길 수 있음.

 

 

 

전역 상태로 인한 메모리/성능 이슈

이슈 유형 설명 대응 방법
데이터가 수천~수만 개 예: works가 10,000건 이상 페이징, 무한스크롤, 서버 캐시
모든 상태 변경이 전체 리렌더 유발 Redux/Context에서 너무 많은 컴포넌트가 구독 Zustand 같은 부분 구독 상태관리 추천
works 자체가 큰 객체 파일 포함, 이미지 base64 등 저장 대신 ID만 들고 있고, 세부 정보는 lazy fetch

 

 

✅ 성능 좋게 전역 상태를 유지하는 팁

1. 필요한 데이터만 저장하기

예: 전체 works 배열 대신 worksMap: { [id]: Work } + workIds: string[] 같이 분리 저장

2. 컴포넌트별로 부분 구독

Redux Toolkit + useSelector (메모이제이션)

Zustand는 기본적으로 부분 구독이라 성능 좋음

3. 서버 캐싱 도구 활용

React Query / SWR로 데이터를 “전역처럼” 쓰고 자동 캐싱/패칭 활용

4. LocalStorage / IndexedDB 연계

너무 큰 데이터는 브라우저 저장소에 offload 가능

 

 

React쪽에서 많이 사용하는 상태관리 방법중에 

거의 설정 필요 없이 바로 사용 가능. 작지만 유연한 구조에 딱! 인 녀석이 Zustand입니다.

 

 

Zustand

 

 

 

 

 

Zustand + 서버 캐싱 조합

// useWorkStore.ts
import { create } from 'zustand';

interface Work {
  id: string;
  title: string;
}

interface WorkStore {
  works: Record<string, Work>;
  setWorks: (works: Work[]) => void;
}

export const useWorkStore = create<WorkStore>((set) => ({
  works: {},
  setWorks: (list) =>
    set({
      works: Object.fromEntries(list.map((w) => [w.id, w])),
    }),
}));

 

이러면:

전역 상태 관리

필요한 데이터만 접근

컴포넌트 리렌더 최소화

 

 

✨ 결론

Works 데이터를 전역으로 관리해도 메모리 문제는 거의 없음,

데이터가 너무 크거나, 빈번하게 바뀌거나, 전체 리렌더를 유발하는 구조라면 성능 최적화 필요.

Zustand, Redux Toolkit, React Query 조합을 잘 쓰면 아주 효율적으로 관리 가능

 

 

 Zustand가 인기 많은 이유

 

장점 설명
심플한 API create() 하나로 상태 만들고 끝. boilerplate 거의 없음
Redux보다 가볍고 직관적 action, reducer, slice 다 필요 없음
부분 상태 구독 가능 리렌더 최적화가 자동으로 됨 (성능 굿)
TypeScript 친화적 타입 추론 잘 되고, 구조도 깔끔
서버 상태와도 궁합 좋음 React Query, SWR 등과 같이 쓰기 쉬움
중간 규모 프로젝트에 딱 복잡하지 않으면서 강력함

 

 

실제 사용하는 곳들

Vercel (Zustand 만든 팀)

T3 Stack (Next.js + TypeScript + Tailwind + Zustand + Prisma)

커뮤니티 기반 SaaS, 사이드 프로젝트 등에서 널리 사용됨

 

 

 

프로젝트 규모 Zustand 적합성
소규모/사이드 프로젝트 👍 아주 적합
중간 규모 SaaS 👍 훌륭함
대규모 기업 프로젝트 👍 가능하지만, 팀의 스타일에 따라 Redux 선호할 수도
글로벌 상태가 많지 않은 앱 ✅ 가볍게 도입 가능

 

비교

항목 Zustand Redux Toolkit Context + useReducer
코드 간결함 ✅ 매우 심플 ❌ 비교적 장황함 ⚠️ 구조화 필요
전역 상태 관리 ✅ 가능 ✅ 아주 강력 ⚠️ 퍼포먼스 이슈 가능
리렌더 최적화 ✅ 기본 내장 ⚠️ 직접 memoization 필요 ❌ 전체 리렌더 발생 가능
타입스크립트 ✅ 굿 ✅ 굿 ⚠️ 직접 타입 정의 많음
학습 난이도 ⭐ 쉬움 ❌ 중간 이상 ⭐ 쉬움

 

정리

Zustand는 2024~2025 기준으로 가장 많이 쓰이는 경량 상태 관리 라이브러리 중 하나임

복잡한 Redux 셋업 없이도 전역 상태를 깔끔하게 다룰 수 있고, 성능도 아주 좋음.

 

 

 

설치하기

npm install zustand

 

 

적용하기

Folder 구조

src/
  features/
    works/
      repositories/
        WorkRepository.ts
      stores/
        useWorkStore.ts
      model/
        work.ts

 

 

WorkRepository.ts (함수형)

// features/works/repositories/WorkRepository.ts
import { supabase } from '../../../common/supabase';
import { table } from '../../../common/config/configstring';
import { Work, WorkView } from '../model/work';

export const WorkRepository = {
  async getList(): Promise<WorkView[]> {
    const { data, error } = await supabase.from(table.WORK_VIEW).select('*');
    if (error) console.error('getList error:', error);
    return data ?? [];
  },

  async getListByAssigneeId(assignee: string): Promise<WorkView[]> {
    const { data, error } = await supabase
      .from(table.WORK_VIEW)
      .select()
      .eq('assignee', assignee);
    if (error) console.error('getListByAssigneeId error:', error);
    return data ?? [];
  },

  async getCountByAssigneeId(assignee: string): Promise<number> {
    const { count, error } = await supabase
      .from(table.WORK_VIEW)
      .select('*', { count: 'exact' })
      .eq('assignee', assignee)
      .limit(0);
    if (error) console.error('getCountByAssigneeId error:', error);
    return count ?? 0;
  },

  async getListByFilter(filter: string, offset = 0, cnt = 10): Promise<{ v: WorkView[]; total: number }> {
    const { data, count } = await supabase
      .from(table.WORK_VIEW)
      .select('*', { count: 'estimated' })
      .or(filter)
      .range(offset, offset + cnt);
    return { v: data ?? [], total: count ?? 0 };
  },

  async getById(id: number): Promise<WorkView | null> {
    const { data, error } = await supabase
      .from(table.WORK_VIEW)
      .select()
      .eq('id', id);
    if (error) console.error('getById error:', error);
    return data?.[0] ?? null;
  },

  async insert(work: Work): Promise<Work | null> {
    const { data, error } = await supabase
      .from(table.WORKS)
      .insert(work)
      .select();
    if (error) console.error('insert error:', error);
    return data?.[0] ?? null;
  },

  async update(work: Work): Promise<Work | null> {
    const { data, error } = await supabase
      .from(table.WORKS)
      .update(work)
      .eq('id', work.id)
      .select();
    if (error) console.error('update error:', error);
    return data?.[0] ?? null;
  },

  async delete(id: number): Promise<void> {
    const { error } = await supabase.from(table.WORKS).delete().eq('id', id);
    if (error) console.error('delete error:', error);
  },
};

 

 

useWorkStore.ts (Zustand)

// features/works/stores/useWorkStore.ts
import { create } from 'zustand';
import { Work, WorkView } from '../model/work';
import { WorkRepository } from '../repositories/WorkRepository';

interface WorkState {
  works: WorkView[];
  selectedWork: WorkView | null;
  totalCount: number;
  loading: boolean;

  fetchWorks: () => Promise<void>;
  fetchWorksByAssignee: (assignee: string) => Promise<void>;
  fetchWorksByFilter: (filter: string, offset?: number, cnt?: number) => Promise<void>;
  fetchWork: (id: number) => Promise<WorkView | null>;

  createWork: (work: Work) => Promise<Work | null>;
  updateWork: (work: Work) => Promise<Work | null>;
  deleteWork: (id: number) => Promise<void>;
}

export const useWorkStore = create<WorkState>((set) => ({
  works: [],
  selectedWork: null,
  totalCount: 0,
  loading: false,

  fetchWorks: async () => {
    set({ loading: true });
    const data = await WorkRepository.getList();
    set({ works: data, loading: false });
  },

  fetchWorksByAssignee: async (assignee: string) => {
    set({ loading: true });
    const data = await WorkRepository.getListByAssigneeId(assignee);
    set({ works: data, loading: false });
  },

  fetchWorksByFilter: async (filter, offset = 0, cnt = 10) => {
    set({ loading: true });
    const { v, total } = await WorkRepository.getListByFilter(filter, offset, cnt);
    set({ works: v, totalCount: total, loading: false });
  },

  fetchWork: async (id: number) => {
    const work = await WorkRepository.getById(id);
    set({ selectedWork: work });
    return work;
  },

  createWork: async (work: Work) => {
    const newWork = await WorkRepository.insert(work);
    return newWork;
  },

  updateWork: async (work: Work) => {
    const updated = await WorkRepository.update(work);
    return updated;
  },

  deleteWork: async (id: number) => {
    await WorkRepository.delete(id);
  },
}));

 

 

 

사용 예시

const { works, fetchWorks, loading } = useWorkStore();

useEffect(() => {
  fetchWorks();
}, []);

return loading ? <p>Loading...</p> : <ul>{works.map(w => <li key={w.id}>{w.title}</li>)}</ul>;

 

 

 

# 해피코딩 !!

반응형

 

1. 지형정보 가져오기

Leaflet 을 사용할때 layer는 보통 geojson을 사용하게 됩니다.

 

지형이나 특정 지역을 표시할때, geojson을 구해서 사용해야 하는데, OSM( open street map)을 이용하면, 이미 만들어진 지역의 정보를 구할수 있습니다. 

https://www.openstreetmap.org/#map=19/37.250567/127.078423

 

오픈스트리트맵

오픈스트리트맵 (OpenStreetMap)은 여러분과 같은 사람들이 만들어, 개방형 라이선스에 따라 자유롭게 사용할 수 있는 세계 지도입니다.

www.openstreetmap.org

 

open street map 접속하기

내보내기

 

OSM file 이 만들어집니다.

 

OSM 파일에는 해당 영역의 모든 데이터가 있습니다.

 

2.osm to geojson(OSM 파일을 geojson으로 바꾸기)

OSM 파일을 geojson으로 변경을 위해서는 이를 지원하는 서비스를 이용하거나 아니면 코드로 작성해야 겠죠..^^ (당연한 이야기죠. )

 

https://mygeodata.cloud/

 

MyGeodata Converter | MyGeodata Cloud

--> --> Uploaded Files Type Size Please note that your data will not be shared to anybody. Recently used data is your data that you have recently uploaded and can only be seen by you from your computer and your web browser. You can reuse them for a repeat

mygeodata.cloud

여기에서OSM 파일을 geojson으로 변경이 가능합니다.

그러나 가격 부담 ㅠ_ㅠ

 

 

그래서 저는 직접 만드는 쪽으로 선회했습니다. ^^

 

import codecs
import json
import osm2geojson

# 각 feature에 bbox 추가
def add_bbox_to_features(geojson):
    for feature in geojson['features']:
        geom = feature.get('geometry')
        if not geom:
            continue
        coords = geom.get('coordinates')

        def extract_coords(c):
            if isinstance(c[0], (float, int)):  # Point
                yield c
            else:
                for sub in c:
                    yield from extract_coords(sub)

        min_lat = min_lon = float('inf')
        max_lat = max_lon = float('-inf')

        for lon, lat in extract_coords(coords):
            min_lon = min(min_lon, lon)
            min_lat = min(min_lat, lat)
            max_lon = max(max_lon, lon)
            max_lat = max(max_lat, lat)

        feature['bbox'] = [min_lon, min_lat, max_lon, max_lat]

# OSM 파일 읽기 및 변환
with codecs.open('sample.osm', 'r', encoding='utf-8') as data:
    xml = data.read()

geojson = osm2geojson.xml2geojson(xml, filter_used_refs=False, log_level='INFO')

# feature 단위 bbox 추가
add_bbox_to_features(geojson)

# 저장
with open("result.json", "w", encoding="utf-8") as f:
    json.dump(geojson, f, ensure_ascii=False, indent=2)

 

 

osm2geojson이라는 python package가 있는데 이를 활용하여 간단하게 만들수 있습니다.

(bbox는 사실 필요 없는데, 저는 bbox 활용할 일이 있어서 추가 작성 했습니다.)

 

osm2geojson 예제에 text encoding 관련된 부분에 한글이 고려 안되어있는 부분이 있습니다. 그래서 unicode 텍스트 (이거 뭐랄까 ascii로 unicode를 하나하나 출력) 출력되는데, 이부분 주의가 필요합니다.

with open("result.json", "w", encoding="utf-8") as f:
    json.dump(geojson, f, ensure_ascii=False, indent=2)

 

아무튼 결과물은 다음과 같이 나오게 됩니다.

결과물 (일부만)

{
      "type": "Feature",
      "properties": {
        "type": "way",
        "id": 60274279,
        "tags": {
          "leisure": "park",
          "name": "반달공원",
          "name:en": "Bandal Park",
          "name:ko": "반달공원"
        },
        "nodes": [
          522283226,
          804659125,
          522283225,
          522283224,
          522283223,
          522283222,
          522283221,
          522283220,
          522283219,
          804658865,
          522283218,
          749481489,
          12597050520,
          1041086129,
          522283226
        ],
        "timestamp": "2025-02-17T23:25:53Z",
        "user": "VLD297",
        "uid": 22440181,
        "version": 6
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              127.0788144,
              37.2516044
            ],
            [
              127.0790588,
              37.2514582
            ],
            [
              127.0792253,
              37.251231
            ],
            [
              127.0792854,
              37.2509749
            ],
            [
              127.0792393,
              37.2507596
            ],
            [
              127.0791239,
              37.2505404
            ],
            [
              127.0786913,
              37.250087
            ],
            [
              127.0784826,
              37.2499406
            ],
            [
              127.0782648,
              37.2498577
            ],
            [
              127.077969,
              37.2498236
            ],
            [
              127.0776801,
              37.2498829
            ],
            [
              127.0773679,
              37.2500504
            ],
            [
              127.0777277,
              37.2504259
            ],
            [
              127.0780778,
              37.250814
            ],
            [
              127.0788144,
              37.2516044
            ]
          ]
        ]
      },
      "bbox": [
        127.0773679,
        37.2498236,
        127.0792854,
        37.2516044
      ]
    },

 

이렇게 나온 geojson을 leaflet 을 이용하여 layer로 추가하면 멋진 커스터마이징이 가능해지죠!!

 

 

# 해피코딩!!

'Node.js , React, Docker' 카테고리의 다른 글

[Typescript] 한글 조사 처리  (2) 2025.04.09
[react] zustand 이용한 store-repository 구조  (0) 2025.03.27
[React] OSM :MAP Tile Server  (0) 2025.03.24
[Docker] NginX docker 환경 만들기  (0) 2024.12.24
[React] Components  (0) 2024.09.07
반응형

 

OSM : Open Street Map

https://planet.openstreetmap.org/

 

Planet OSM

Supporting OSM OSM data is free to use, but is not free to make or host. The stability and accuracy of OSM.org depends on its volunteers and donations from its users. Please consider making an annual recurring gift to OSM to support the infrastructure, too

planet.openstreetmap.org

 

 

구글 맵, 다음 지도, 네이버 맵등을 이용해서 지도 정보를 화면에 출력할 수도 있지만, OSM(Open Street Map)을 이용해보고자 합니다.

(뭔가 더 자유도가 있는 것 같음..)

 

 

OSM 은 우선 사용하는데에는 무료 입니다.

OSM는 어떻게 무료인가요?

OpenStreetMap은 크라우드소싱으로 만들어진 오픈 데이터베이스

ODbL(Open Database License) 하에 무료로 사용할 수 있습니다.

누구든지 상업/비상업 목적으로 자유롭게:

데이터를 보고,

복사하고,

수정하고,

앱/웹에서 사용할 수 있어요.

 

하지만 다음은 꼭 주의하세요

항목설명

🔌 타일 서버 (tile.openstreetmap.org) OSM 데이터는 무료지만 기본 타일 서버는 “공공 기여 기반”이라 과도한 사용 금지
📈 상업적 서비스 웹사이트/앱에서 많은 트래픽이 예상되면 타일 서버를 직접 운영하거나 외부 제공업체(MapTiler 등)를 사용해야 함
📝 저작권 표시 필수 지도에 “© OpenStreetMap contributors” 표기 필요
💥 Rate Limit 기본 서버는 과도한 호출 시 차단당할 수 있음 (특히 앱에서 다수 사용자일 경우)

 

 

 대안 서비스 (타일 서버 대체용)

서비스설명

MapTiler 상업적 사용 OK, 다양한 스타일 제공
OpenMapTiles 자체 호스팅 가능, OSM 기반
Carto 지도 + 데이터 시각화 전문
Mapbox 고성능 상업용 지도 SDK (유료 요금제 있음)

 

 

 

 

실무 예시

작은 규모의 내부 대시보드나 개인 프로젝트 → tile.openstreetmap.org 사용 OK

상업 서비스, 앱 배포, 사용자 많은 시스템 → 별도 타일 서버나 상용 서비스 이용 권장

 

 

OSM 타일 서버 직접 구축하는 법

도구설명

TileServer GL 벡터/래스터 타일 서버, Docker로 간편 구축 가능
Tegola Go 기반 고성능 벡터 타일 서버
openmaptiles.org 미리 생성된 벡터 타일 다운로드 or Docker 배포

 

 

타일 서버 구축

1. 프로젝트 디렉토리 구성

osm-tileserver/
├── docker-compose.yml
└── data/
    └── south-korea.mbtiles   ← 원하는 지역 타일 파일

 

2. docker-compose.yml

 

version: "3"

services:
  tileserver:
    image: klokantech/tileserver-gl
    ports:
      - "8080:80"
    volumes:
      - ./data:/data
    environment:
      - MAPBOX_STYLES=local

 

3. 실행 방법

# 프로젝트 폴더로 이동
cd osm-tileserver

# 실행
docker-compose up -d

 

 

 

4. 접속 확인

 

브라우저에서 접속: http://localhost:8080

 

제공 기능:

웹 기반 지도 뷰어

벡터 타일 (PBF)

래스터 PNG 타일 (/styles/basic/{z}/{x}/{y}.png)

Leaflet/OpenLayers 연동용 URL 자동 제공

 

5. Leaflet 연동 예시

L.tileLayer('http://localhost:8080/styles/basic/{z}/{x}/{y}.png', {
  maxZoom: 18,
  attribution: '© OpenStreetMap contributors'
}).addTo(map);

 

 

.mbtiles 파일은 어디서 받나요?

https://openmaptiles.org/downloads/

대한민국: south-korea.mbtiles 다운로드

용량 수백 MB ~ 수 GB

 

 

6. OSM file을 geojson 으로 변경하기

OSM 맵의 특정 부분을 geojson으로 변경해서 사용하고 싶을 때가 있습니다.

특정 영역의 색상을 표시한다거나 할때, 지도상에서 해당 영역을 찾아서 표시 하고 싶을 때가 있죠.

 

 

# 해피코딩 !!

 

 

 

 

+ Recent posts