blogger template language / 구글 블로거 태그
크롬에서 창바꾸기 / firefox 처럼 switch windows / switch tabs
QuicKey 라는 extension 이 이 기능을 제공한다.
아래 링크로 가면, 크롬의 ‘키보드 단축키’(Keyboard shortcuts)에서 Ctrl+Tab
에 대한 설정을 어떻게 하는 지 알려준다. 글을 보면 javascript 를 이용해서 한다.
chrome.developerPrivate.updateExtensionCommand({
extensionId: "ldlghkoiihaelfnggonhjnfiabmaficg",
commandName: "010-open-popup-window",
keybinding: "Ctrl+Tab"
});
참고로 이전의 ctrl + tab
으로 tab 을 이동하는 것은 ctrl + page up
, ctrl + page down
을 이용하면 된다.
window 11 데브 드라이브 / 개발자 드라이브 / windows 11 /
기존의 할당되어 있지 않은(unallocated) disk 를 사용하거나, 새롭게 unallocated disk 를 생성해야 한다.
disk 를 생성하는데는 2가지 방법이 있다.
여기서는 VHD 만들기를 사용한다. 가상 disk 를 생성하는 방법이다.
diskmgmt.msc
: 디스크관리 메뉴로 들어간다.Format e: /DevDrv /Q
dev drive 에는
Mount-DiskImage -ImagePath "d:\a\vmbox\devDrive\dev_drive.vhdx"
Dismount-DiskImage -ImagePath "d:\a\vmbox\devDrive\dev_drive.vhdx"
Mount-DiskImage -ImagePath "d:\a\vmbox\devDrive\dev_drive.vhdx"
누끼 프로그램 / 앱 / desktop app / 유틸 / 툴 / 도구 /
마크다운으로 문서 작성 / 워드 / 한글 / 이력서 작성
markdown 으로 글을 쓴다. 여기서는 파일 이름을 resume.md
라고 가정하자.
html 로 변환 : Pandoc 사용
rem resume.md --> resume.html
pandoc -f markdown -t html < resume.md > resume.html
resume.html
에 css 적용
resume.html
의 다음 내용을 추가한다.<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
...
</style>
</head>
<body>
{{ ... }}
</body>
</html>
pdf 로 변환 : wkhtmltopdf 사용
footer.html
에 page no 를 추가함.footer.html
를 넣고 header를 따로 지정하지 않으면, header 쪽의 여백까지도 사라져버려서 header.html
을 지정해줬다.rem `resume.html` --> `output.pdf`
"c:\myapps\wkhtmltopdf.exe" --header-html header.html --footer-html footer.html --page-size A4 "d:\a\etc\resume\resume.html" f:\output.pdf
여러 language 에서 사용할 수 있는 library 도 있지만, 바로 사용할 수 있는 exe도 제공한다.
--header-html
, --footer-html
에 대한 부분을 확인하자.
윈도우즈
설정 -> 앱 -> 고급 앱 설정 -> 앱 실행 별칭
에 있는 별칭들은 다음 경로에 있다.
%LocalAppData%\Microsoft\WindowsApps
이것을 추가로 만들려면 mklink
로 exe 에 대한 symbolic link 를 만들어준다.
mklink %LocalAppData%\Microsoft\WindowsApps\my_alias_name.exe "the_full_path_of_your_executable_file"
참고로, 이렇게 하면 symbolic link 는 만들어지지만, ’앱 실행 별칭’에서 보이진 않는다.
윈도우즈 URI / windows11 app uri
windows 에서도 app 에 대한 URI 를 사용할 수 있다. 기본적으로 윈도우즈의 기본앱에 대한 URI 는 아래링크에서 확인할 수 있다.
아래는 자신의 컴퓨터의 모든 URI scheme 을 확인할 수 있는 .bat file 의 내용이다. 필자의 컴퓨터에서는 대략 6분정도 걸렸다.
@For /f "tokens=1* delims=" %%A in ('reg query HKCR /f "URL:*" /s /d ^| findstr /c:"URL:" ^| findstr /v /c:"URL: " ^| Sort') Do @Echo %%A %%B
pause
만약 ms-photo
를 실행하려면, start ms-photo:
로 실행할 수 있다.
reg query HKCR /f "URL:*"
위 batch 를 실행한 결과값의 일부이다. ms 와 관계있는 URI 들을 모아놨다.
...
(기본값) REG_SZ URL:microsoft-edge
(기본값) REG_SZ URL:microsoft.windows.camera
(기본값) REG_SZ URL:microsoft.windows.camera.picker
(기본값) REG_SZ URL:microsoft.windows.photos.crop
(기본값) REG_SZ URL:microsoft.windows.photos.picker
(기본값) REG_SZ URL:microsoft.windows.photos.videoedit
(기본값) REG_SZ URL:MicrosoftBing Protocol
(기본값) REG_SZ URL:microsoftmusic
(기본값) REG_SZ URL:microsoftvideo
(기본값) REG_SZ URL:MK Protocol
(기본값) REG_SZ URL:mms Protocol
(기본값) REG_SZ URL:mms Protocol
(기본값) REG_SZ URL:ms-aad-brokerplugin
(기본값) REG_SZ URL:ms-actioncenter
(기본값) REG_SZ URL:ms-appinstaller
(기본값) REG_SZ URL:ms-apprep
(기본값) REG_SZ URL:ms-calculator
(기본값) REG_SZ URL:ms-clipchamp
(기본값) REG_SZ URL:ms-clock
(기본값) REG_SZ URL:ms-contact-support
(기본값) REG_SZ URL:ms-cortana2
(기본값) REG_SZ URL:ms-crossdevice-files
(기본값) REG_SZ URL:ms-crossdevice-settings
(기본값) REG_SZ URL:ms-crossdevice-share
(기본값) REG_SZ URL:ms-cxh
(기본값) REG_SZ URL:ms-default-location
(기본값) REG_SZ URL:ms-devhome
(기본값) REG_SZ URL:ms-device-enrollment
(기본값) REG_SZ URL:ms-device-enrollment2
(기본값) REG_SZ URL:ms-drive-to
(기본값) REG_SZ URL:ms-edu-secureassessment
(기본값) REG_SZ URL:ms-eyecontrolspeech
(기본값) REG_SZ URL:ms-gamebar
(기본값) REG_SZ URL:ms-gamebarservices
(기본값) REG_SZ URL:ms-gamingoverlay
(기본값) REG_SZ URL:ms-get-started
(기본값) REG_SZ URL:ms-getoffice
(기본값) REG_SZ URL:ms-holographicfirstrun
(기본값) REG_SZ URL:ms-inputapp
(기본값) REG_SZ URL:ms-insights
(기본값) REG_SZ URL:ms-ipmessaging
(기본값) REG_SZ URL:ms-launchremotedesktop
(기본값) REG_SZ URL:ms-lwh
(기본값) REG_SZ URL:ms-meetnow
(기본값) REG_SZ URL:ms-meetnowflyout
(기본값) REG_SZ URL:ms-msdt
(기본값) REG_SZ URL:ms-newsandinterests
(기본값) REG_SZ URL:ms-notepad
(기본값) REG_SZ URL:ms-officeapp
(기본값) REG_SZ URL:ms-officecmd
(기본값) REG_SZ URL:ms-oobenetwork
(기본값) REG_SZ URL:ms-outlook
(기본값) REG_SZ URL:ms-paint
(기본값) REG_SZ URL:ms-pchealthcheck
(기본값) REG_SZ URL:ms-penworkspace
(기본값) REG_SZ URL:ms-people
(기본값) REG_SZ URL:ms-phone
(기본값) REG_SZ URL:ms-photos
(기본값) REG_SZ URL:ms-powerautomate
(기본값) REG_SZ URL:ms-print-addprinter
(기본값) REG_SZ URL:ms-print-printjobs
(기본값) REG_SZ URL:ms-print-queue
(기본값) REG_SZ URL:ms-quick-assist
(기본값) REG_SZ URL:ms-retaildemo-launchbioenrollment
(기본값) REG_SZ URL:ms-retaildemo-launchstart
(기본값) REG_SZ URL:ms-screenclip
(기본값) REG_SZ URL:ms-screensketch
(기본값) REG_SZ URL:ms-search
(기본값) REG_SZ URL:ms-settings
(기본값) REG_SZ URL:ms-settings-airplanemode
(기본값) REG_SZ URL:ms-settings-bluetooth
(기본값) REG_SZ URL:ms-settings-cellular
(기본값) REG_SZ URL:ms-settings-emailandaccounts
(기본값) REG_SZ URL:ms-settings-language
(기본값) REG_SZ URL:ms-settings-location
(기본값) REG_SZ URL:ms-settings-lock
(기본값) REG_SZ URL:ms-settings-mobilehotspot
(기본값) REG_SZ URL:ms-settings-notifications
(기본값) REG_SZ URL:ms-settings-power
(기본값) REG_SZ URL:ms-settings-privacy
(기본값) REG_SZ URL:ms-settings-proximity
(기본값) REG_SZ URL:ms-settings-screenrotation
(기본값) REG_SZ URL:ms-settings-wifi
(기본값) REG_SZ URL:ms-settings-workplace
(기본값) REG_SZ URL:ms-stickereditor
(기본값) REG_SZ URL:ms-sttoverlay
(기본값) REG_SZ URL:ms-taskswitcher
(기본값) REG_SZ URL:ms-teams
(기본값) REG_SZ URL:ms-to-do
(기본값) REG_SZ URL:ms-todo
(기본값) REG_SZ URL:ms-unistore-email
(기본값) REG_SZ URL:ms-voip-call
(기본값) REG_SZ URL:ms-voip-video
(기본값) REG_SZ URL:ms-walk-to
(기본값) REG_SZ URL:ms-wcrv
(기본값) REG_SZ URL:ms-widgets
(기본값) REG_SZ URL:ms-windows-search
(기본값) REG_SZ URL:ms-windows-store
(기본값) REG_SZ URL:ms-windows-store-deskext
(기본값) REG_SZ URL:ms-windows-store2
(기본값) REG_SZ URL:ms-windowsbackup
(기본값) REG_SZ URL:ms-wpc
(기본값) REG_SZ URL:ms-wpdrmv
(기본값) REG_SZ URL:ms-wxh
(기본값) REG_SZ URL:ms-xbet-survey
(기본값) REG_SZ URL:ms-xgpueject
...
작업 표시줄 / 위치 / 위에 두기 /windows11
windows11로 update 하고나서 문제는 작업표시줄(taskbar) 를 아래에 고정시켜야 한다는 점이었다. 개인적으로 위에 두는 것을 선호했다.
ref.1 에서 해결할 방법 3가지를 알려준다.
다음 page에서 download 하자.
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\ExplorerPatcher
에 가면 ’ExplorerPatcher’의 속성을 여는 바로가기를 찾을 수 있다.
C:\Windows\System32\rundll32.exe "C:\Program Files\ExplorerPatcher\ep_gui.dll",ZZGUI
를 ’실행’하면 ’속성’창이 열린다.이 속성창에서 ’작업표시줄 스타일’을 Windows 10 으로 변경한다.
작업표시줄 위치도 ’상단’으로 변경한다.
’파일 탐색기’를 다시 시작한다. 왼쪽하단 버튼을 눌러서 다시시작해도 되고, task manager에서 explorer를 죽이고, 다시 실행해도 된다.
왼쪽메뉴 ‘시작 메뉴’(Start Menu)에서 시작메뉴 스타일도 windows 10 으로 변경할 수 있다.
windows11 build 26002+ 에서 windows 10 taskbar 를 사용할 수 없다고 한다.
이제 다시 windows 10 taskbar를 구현해서 넣었다고한다.
참고로 24H2 버전은 아직 preview 이다.
프로그래밍을 계속 할 수 있을까
eocnomist 의 기사다. 이전의 How AI models are getting smarter 에 이어지는 기사다. (참고: 생성형 AI 에 대한 대략적인 설명)
AI 가 인간을 대체할 수 있을까? 그리고 프로그래머는 이제 곧 사라질까? 이런 질문들에 대한 어느정도의 답이 된다.
from : LLMs will transform medicine, media and more
의학, 미디어 등을 변화시킬 LLM / 하지만 (인간의) 도움 없이는 불가능합니다.
인공지능(AI)는 컴퓨터가 어떤 일을 똑똑하게(인간기준으로) 하는 기술
그런 의미에서 이미 AI 는 많이 쓰이고 있다. 네비게이션의 길찾기, 과속차량 번호판의 인식, 온도 조절기등은 모두 AI 에 속합니다.
AI 가 일관되고, 안정적으로 작동하면, 그것은 그냥 엔지니어링이라 부릅니다.
현재 전 세계의 주목을 받으며 엄청난 양의 컴퓨팅 파워와 전력을 빨아들이고 있는 인공지능은 딥러닝이라는 기술을 기반으로 합니다.
딥러닝에서는 선형대수(특히 행렬 곱셈)와 통계를 사용하여,
학습과정에서 대규모 데이터 세트에서 패턴을 추출하고 그래서 학습하는 것이 됩니다.
Google의 Gemini나 OpenAI의 GPT와 같은 대규모 언어 모델(LLM)은 수많은 텍스트, 이미지, 동영상으로 학습되어 왔다. 그리고 많은 능력을 개발한 상태입니다.
이미지, 음악, 로봇공학, 유전체학, 의학, 기후, 날씨, 소프트웨어 코딩등, 보다 전문화된, 도메인에 특화된 버전의 모델이 존재합니다.
연구자들은 여전히 AI가 무엇을 할 수 있고 무엇을 할 수 없는지 파악하고 있습니다. 지금까지는 더 많은 데이터로 학습된 더 큰 모델이 더 뛰어난 능력을 발휘하는 것으로 입증되었습니다. 이는 계속해서 더 많은 데이터를 추가하면 더 나은 AI를 만들 수 있다는 믿음을 심어주었습니다. 모델 크기와 학습 데이터의 양이 어떻게 상호 작용하여 LLM을 향상시키는지 보여주는 ’확장 법칙’에 대한 연구가 수행되었습니다.
“더 나은” LLM이란 무엇일까요? 질문에 정답을 맞히거나 창의적인 아이디어를 제시하는 모델일까요?
지금까지 AI의 힘은 개별 작업에서 가장 두드러집니다.
하지만 open-ended task에서의 성능은 평가하기가 더 어렵습니다.
오늘날의 대규모 AI 모델은 학습 데이터에 표현된 패턴을 기반으로 시(poetry)에서 실사 이미지에 이르기까지 다양한 것을 생성하는 데 매우 능숙합니다.
그러나 이러한 모델은 생성한 것 중 어떤 것이 가장 의미가 있거나 주어진 상황에 가장 적합한지를 판단하는 데는 능숙하지 못합니다.
논리와 추론 능력도 떨어집니다. AI에게 일관되게 추론할 수 있는 능력을 제공하기 위해 더 많은 데이터를 주면 될 것인지, 아니면 완전히 다른 종류의 모델이 필요한 것인지에 대해서는 아직 명확치 않다.
오랜 시간 동안 AI의 한계가 존재할 수 있다. 그로 인해 이를 활용하기 위해서는 인간의 추론이 필요할 것이다
이러한 한계가 무엇인지 알아내는 것은 의료와 같은 분야에서 중요합니다. AI를 적절히 활용하면 암을 조기에 발견하고, 서비스에 대한 접근성을 확대하고, 진단을 개선하고, 개인 맞춤형 치료를 제공할 수 있습니다. 4월에 발표된 메타 분석에 따르면 AI 알고리즘은 이러한 작업에서 인간 임상의보다 더 나은 성과를 낼 수 있습니다. 하지만 인공지능의 훈련이 그들의 길을 잃게 할 수 있다. 즉, 사람이 개입하는 것을 제한하는 방향으로 훈련될 수도 있다.
예를 들어, AI 모델은 ‘데이터 분포 변화’(“data distribution shifts”)로 인해 더욱 부각된 인간의 편견에 취약한데, 백인의 피부 이미지로 주로 학습한 후 흑인의 피부 이미지를 제공하면 진단 모델이 실수를 할 수 있습니다.
'AI'와 '자격을 갖춘 사람'을 결합하는 것이 가장 효과적인 것으로 입증되었습니다. 이 논문에 따르면 AI를 사용한 임상의는 정확하게 암이라고 진단한 사람의 비율을 81.1%에서 86.1%로 늘릴 수 있었으며, 암이 없다고 정확하게 진단한 사람의 비율도 늘릴 수 있었습니다.
AI 모델은 인간과 다른 실수를 저지르는 경향이 있기 때문에 AI와 인간의 파트너십이 AI 단독으로 일을 하거나, 인간 단독으로 일을 하는 것보다 더 뛰어난 성과를 보이는 것으로 나타났습니다.
로봇방식
과학 분야에서 새로운 가설을 탐구하는 데 지금보다 사람이 덜 필요할 수도 있습니다. 2009년 케임브리지 대학교의 로스 킹 박사는 자신의 궁극적인 목표는 자율 실험실, 즉 ’로봇 과학자’로 기능하는 시스템을 설계하는 것이라고 말했습니다. 킹 박사가 개발한 AI 과학자 아담은 가설을 세우고, 로봇 팔을 사용해 실험을 수행하고, 센서를 통해 결과를 수집하고 분석하도록 설계되었습니다. 대학원생이나 박사후 연구원과 달리 아담은 식사나 수면을 위해 휴식을 취할 필요가 없습니다. 그러나 이러한 유형의 AI 시스템은 현재로서는 신약 개발 및 재료 과학과 같은 비교적 좁은 영역으로 제한되어 있습니다. 인간이 주도하는 연구보다 훨씬 더 많은 이득을 가져다줄 수 있을지는 아직 불분명합니다.
AI 기술은 수십 년 동안 과학 분야에서 데이터를 분류, 선별, 분석하고 예측하는 데 사용되어 왔습니다. 예를 들어, Project CETI의 연구원들은 고래 발성 데이터 세트를 수집한 다음 이 데이터로 AI 모델을 학습시켜 어떤 소리가 의미를 가질 수 있는지 알아냈습니다. 또는 구글 딥마인드에서 개발한 심층 신경망인 ’알파폴드’를 생각해 보세요. 방대한 단백질 데이터베이스로 훈련된 이 시스템은 사람이 며칠 동안 세심하게 실험하고 측정해야 했던 단백질의 3차원 모양을 빠르고 정확하게 예측할 수 있습니다. 딥마인드에서 개발한 또 다른 AI 시스템인 GNoME는 특정 화학적 특성을 가진 새로운 물질을 발견하는 데 도움을 주기 위한 것입니다(다이어그램 참조).
AI는 입자 충돌기의 결과를 선별하여 새로운 아원자 입자(subatomic particles)를 식별하거나, 과학 문헌을 따라잡는 등 연구자가 감당하기 힘든 대량의 데이터 흐름을 이해하는 데도 도움이 될 수 있습니다. 아무리 세심한 독서가라도 자신의 연구와 관련이 있을 수 있는 모든 과학 논문을 소화하는 것은 불가능에 가깝습니다. 이른바 문헌 기반 발 시스템(literature-based discovery system)은 이러한 산더미 같은 텍스트를 분석하여 연구의 빈틈을 찾아내고, 오래된 아이디어를 새로운 방식으로 결합하거나 새로운 가설을 제안할 수 있습니다.
하지만 이러한 유형의 AI 작업이 유익한지 여부를 판단하기는 어렵습니다. AI는 예상치 못한 연역적인 도약을 하는 데 인간보다 더 뛰어나지 않을 수도 있고, 대신 단순히 전통적이고 잘 알려진 연구 경로를 선호하여 흥미로운 결과를 내지 못할 수도 있다
교육계에서는 AI, 특히 ChatGPT와 같은 봇이 오히려 독창적인 사고를 방해할 수 있다는 우려가 있습니다. 2023년 교육 기업 Chegg의 조사에 따르면 전 세계 학생의 40%가 학교 과제를 할 때 AI를 사용했으며, 주로 작문을 위해 사용했습니다. 이로 인해 일부 교사, 교수, 학군에서는 AI 챗봇 사용을 금지하고 있습니다.
많은 사람들이 챗봇을 사용하면 문제를 해결하거나 논쟁을 벌이는 과정에서 문제 해결 능력과 비판적 사고력을 키우는 데 방해가 될 수 있다고 우려하고 있습니다.
다른 교사들은 완전히 다른 접근 방식을 취하여 AI를 도구로 받아들이고 과제에 통합하기도 합니다. 예를 들어, 학생들에게 ChatGPT를 사용하여 주제에 대한 에세이를 작성한 다음 틀린 부분에 대해 비평하도록 요청할 수 있습니다.
잠깐만요, 챗봇이 이 글을 쓴 건가요?
오늘날의 생성형 AI는 버튼 클릭 한 번으로 텍스트를 생성할 뿐만 아니라 이미지, 오디오, 동영상을 단 몇 초 만에 생성할 수 있습니다. 이는 팟캐스트에서 비디오 게임, 광고에 이르기까지 미디어 비즈니스의 판도를 바꿀 수 있는 잠재력을 가지고 있습니다.
AI 기반 도구는 편집을 간소화하고 시간을 절약하며 진입 장벽을 낮출 수 있습니다.
하지만 AI로 제작된 콘텐츠는 일러스트레이터나 성우와 같은 일부 아티스트들을 위험에 빠뜨릴 수 있습니다. 시간이 지나면 인간 배우의 AI 기반 복제품(simulacrum) 또는 완전히 인공적인 배우를 사용하여 영화 전체를 제작하는 것이 가능할 수도 있습니다.
하지만 AI 모델은 스스로 문제를 만들거나 해결할 수 없습니다(아직까지는). 인공지능은 정교한 소프트웨어일 뿐, 지각 능력이 있거나 자율적인 존재가 아닙니다. AI는 자신을 호출하고, 자신에게 묻고, 그 결과를 적용 또는 폐기하는 것을 하는데에 있어 인간 사용자에게 의존합니다. 좋든 나쁘든 AI의 혁신적인 능력은 여전히 인간과 인간의 판단에 달려 있습니다.
generative AI / 설명 / 간략설명 / 이해
경제지 economist 에서 생성형AI 에 대해 간략하게 이야기해주고 있다. 생성형 AI 가 어떻게 동작하는지 이해를 도와줘서 옮겨놓는다. 개인적으로 필요한 부분만 옮겨놓은 것이라서, 완전한 글은 본문을 참고하자.
from : How AI models are getting smarter, 2024-08-06
뉴런들을 네트워크에 연결하고, 뉴런들을 네트워크에 계층화하는 여러방법이 있다.
이런 아키텍쳐의 발전이 좀 더 학습이 쉽고, 좀 더 나은 결과를 가져다 주는 신경망을 만드는 데 도움이 됐다.
현재 관심은 text를 위한 LLM(Large language model)과 image를 위한 diffusion model 들에 집중되어 있다.
이 모델들은 이전 모델들보다 더 많은 뉴런층을 가지고 있으며, 방대한 데이터를 빠르게 처리할 수 있는 방식으로 구성됐다.
transformer architecture 를 기반으로 LLM 들이 만들어졌다. 트랜스포머는 2017년에 Google Brain의 아시시 바스와니와 그의 팀이 소개했는데, 이 트랜스포머의 핵심 원리는 ‘attention’(주의) 이다.
attention layer는 모델이 입력값들의 여러 측면들 — 예를 들면, 텍스트에서 서로 일정한 거리에 있는 단어들 — 이 서로 어떻게 연결되어 있는지를 학습할 수 있게 해준다. 그리고 이를 output을 만드는 과정에 반영할 수 있도록 해준다.
한 row 에 많은 attention layer들이 있으면, 모델은 단어, 구(phrase) 심지어 문단 사이에 있는 다양한 수준의 세밀한부분에서(at different levels of granularity) 연관성을 학습할 수 있게 된다.
transformer 기반의 모델들은 text뿐만 아니라 image 도 생성할 수 있다.
OpenAI 가 2021년에 출시한 DALL-E 의 첫번째 버전은 이미지의 픽셀 그룹간의 연관성을 학습하는 트랜스포머였다.
신경망은 그것이 보는 것을 숫자로 변환하고, 이에 대해 수학(특히, 행렬연산)을 행한다.
트랜스포머에는 한계가 있다. 일관되게 세계(world)를 표현하는 model 을 학습하는 것이 어렵다.
이 트랜스포머 기반 모델은 인간의 질문에 답할 때, 한 답변에서 다음 답변으로 넘어가면서 스스로 모순된 답변을 할 수 있다. 이것은 첫번째 답변이 두 번째 답변을 모순되게 만든다는 것을 이해하지 못하기 때문이다.
이 모델은 실제로 그 답변을 이해해서 알고있는 것이 아니라, 단순히 답변처럼 보이는 특정 문자열들의 연관성만 알고 있기 때문이다.
트랜스포머 기반 모델은 잘못된 답을 만들고, 그럴 듯한 가짜 인용을 만들어내는 ‘환각’(hallucinations)이 발생하기 쉽다.
마찬가지로, 초기 트랜스포머 기반 모델에서 생성된 이미지는 종종 물리 법칙을 위반하고 받아드리기 어려운 경우가 많았다(일부 사용자에게는 기능일 수 있지만 사실적인 이미지를 생성하려는 디자이너에게는 버그였다). 다른 종류의 모델이 필요했다.
diffusion model(확산 모델)들은 훨씬 더 사실적인 이미지를 생성할 수 있다.
diffusion model의 주요아이디어는 확산의 물리적과정에서 영감을 받았다. 뜨거운 물 한 컵에 티백을 넣으면 찻잎이 가파르게 가라앉기 시작하고, 차의 색이 맑은 물로 스며들어 흐려진다. 몇 분 동안 그대로 두면 컵에 담긴 액체가 균일한 색을 띠게 된다.
이 확산 과정은 물리 법칙에 의해 결정된다. 물리 법칙을 사용하여 차가 어떻게 확산될지 예측할 수 있는 것처럼, 이 과정을 역설계하여 티백이 처음 어디에 어떻게 떨어졌을지 재구성할 수도 있다. 현실에서는 이미 벌어진 일을 뒤로 돌릴 수 없지만, 엔트로피를 역전시키는 과정을 시뮬레이션하는 법을 학습하면 사실적인 이미지 생성이 가능해진다.
훈련(training)은 다음과 같이 작동한다.
이미지를 가져와서 완전히 무작위로 보일 때까지 점점 더 많은 흐림(blur)과 노이즈를 적용합니다. 그 다음, 이부분이 어려운 부분인데, 이 과정을 역으로 진행하여 차에서 티백을 찾아내는 것처럼 원본 이미지를 다시 만드는 것이다.
이 작업은 “self-supervised learning”(자기 지도 학습)을 통해 이뤄진다. text에서 LLM이 훈련되는 방식과 유사하다.
그것은 문장에서 단어를 가리고, 시도와 오류(trial and error)를 통해 누락된 단어를 예측하는 방법이다.
이미지의 경우, 네트워크는 원본 이미지를 재생산하기 위해 점점 더 많은 양의 노이즈를 제거하는 방법을 학습한다. 수십억 개의 이미지를 처리하면서 왜곡을 제거하는 데 필요한 패턴을 학습하게 되면, 네트워크는 아무것도 없는 무작위 노이즈에서 완전히 새로운 이미지를 생성할 수 있는 능력을 얻게 된다.
대부분의 최첨단 이미지 생성 시스템은 디퓨전 모델을 사용하지만, 노이즈 제거 또는 왜곡을 역전(reversing distortions)시키는 방식은 서로 다르다.
2022년에 출시된 Stable Diffusion(Stability AI의)과 Imagen은 모두 컨볼루션 신경망(CNN)이라는 아키텍처의 변형을 사용했습니다. CNN은 그리드 형태의 데이터를 분석하는 데 뛰어납니다. 픽셀의 행과 열 같은.
실제로 CNN은 입력된 데이터에서 작은 슬라이딩 창을 위아래로 움직이며 패턴이나 모서리와 같은 특정 객체(artefacts)를 찾는다.
CNN은 픽셀에서 잘 작동하지만, 최신 이미지 생성기 중 일부는 확산 트랜스포머(difffusion transformers)를 사용한다. 스테이빌리티 AI의 최신 모델인 Stable Diffusion 3가 여기에 포함된다. 확산에 대한 훈련을 받은 트랜스포머는 이미지나 비디오 프레임의 다양한 부분이 서로 어떻게 연관되어 있는지, 그리고 얼마나 강하게 또는 약하게 연결되어있는지를 훨씬 더 잘 알아낼 수 있어서 보다 사실적인 결과물을 만들어낸다.(여전히 실수가 있긴 하지만).
추천시스템(Recommendation systems) 은 다른 문제다.(kettle of fish)
이러한 시스템의 추천 알고리즘을 구축하고 사용하는 회사들은 추천 알고리즘에 대해 극도로 비밀을 유지하기 때문에 내부 구조를 엿볼 기회는 드물다.
그러나 2019년 Meta(당시 Facebook)는 자사의 딥러닝 추천 모델(DLRM)에 대한 세부 정보를 공개했다. 이 모델은 세 가지 주요 부분으로 구성되어 있다.
비슷한 방식은 광고, 스트리밍 서비스의 노래, 이커머스 플랫폼의 제품 등에도 적용할 수 있다.
기술 기업들은 이와 같이 상업적으로 유용한 작업을 잘 수행하는 모델에 가장 큰 관심을 보입니다. 하지만 이러한 모델을 대규모로 실행하려면 막대한 비용과 방대한 양의 데이터, 엄청난 처리 능력(processing power)이 필요합니다.
학술적 맥락에서는, 데이터셋이 작고 예산이 제한적이기 때문에 다른 종류의 모델이 더 실용적입니다.
이러한 모델에는 순환 신경망(데이터의 시퀀스를 분석하기 위해 사용됨), 변이형 오토인코더(variational autoencoders, VAE)(데이터에서 패턴을 발견하기 위해 사용됨), 생성형 적대 신경망(generative adversarial networks, GAN)(하나의 모델이 다른 모델을 반복적으로 속이면서 작업을 배우는 방식), 그래프 신경망(복잡한 상호작용의 결과를 예측하기 위해 사용됨)이 포함된다.
심층 신경망, 트랜스포머, 확산 모델이 모두 연구적 호기심에서 광범위한 배포로 발전한 것처럼, 이러한 다른 모델의 특징과 원리는 미래의 AI 모델에 활용되고 통합될 것이다. 트랜스포머는 매우 효율적이지만, 이를 확장한다고 해서 환각과 추론 시 논리적 오류를 범하는 경향을 해결할 수 있는지는 확실하지 않다. 이러한 약점을 극복하고 다음 단계로 나아갈 수 있는 “상태 공간 모델(state-space models)”부터 “신경-상징적(neuro-symbolic)” AI에 이르는 ‘포스트 트랜스포머’ 아키텍처에 대한 탐색이 이미 진행 중이다. 이상적으로는 이러한 아키텍처가 더 뛰어난 추론 능력을 겸비한 주의(attention)을 갖는 것이 이상적이다. 현재로서는 그런 모델을 어떻게 구축할지 아는 사람은 없습니다. 언젠가는 AI 모델이 이 작업을 해낼 수도 있을 것이다.
벡터 지도 / 웹에서 지도 그리기 / d3 / mapping shape files/ cartography
Shpefile 또는 GeoJSON 등의 파일을 얻었으면, mapshaper 에서 이것이 어떻게 그려지는지를 확인해 볼 수 있다.
npm install -g d3-geo-projection
windows 에선 "
를 사용하지만, bash 에서는 '
를 사용하면 된다.
rotate 는 반시계방향이다. conic projection 이어서 rotate 가 정확히 평면에서 rotate 하는 것과는 조금 다르다. 원추도법(Conic projection) 에 대한 설명은 wiki page](https://ko.wikipedia.org/wiki/%EC%A7%80%EB%8F%84_%ED%88%AC%EC%98%81%EB%B2%95#%EC%9B%90%EC%B6%94%EB%8F%84%EB%B2%95)를 참고하자.
parallels
는 표준위선이다. 이 표준위선내에서는 축척이 일정하다고 볼 수 있다. 원뿔모양이라서 표준위선 밖은 위로갈수록 축척이 커지고, 아래로 갈수록 축척이 작아진다..\node_modules\.bin\geoproject.cmd "d3.geoConicEqualArea().parallels([33, 38.7]).rotate([230, 0]).fitSize([960, 960], d)" < south-korea-with-regions_1516.geojson > korea-map.json
.\node_modules\.bin\geo2svg.cmd -w 960 -h 960 < korea-map.json > korea-map.svg
korea-map.svg
를 열어보면 된다.
graphql / code first
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
nest generate module brand
nest generate resolver brand
nest generate service brand
src/app.module.ts
에 GraphQL module 설정 (아래 ‘codes’ 참고)/src/brand/brand.type.ts
생성 (아래 ‘codes’ 참고)/src/brand/brand.service.ts
(아래 ‘codes’ 참고)/src/brand/brand.resolver.ts
(아래 ‘codes’ 참고)npm run start
localhost:3000/graphql
// app.modules.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { BrandModule } from './brand/brand.module';
import { join } from 'path';
@Module({
imports: [
BrandModule,
// graphql module 설정
GraphQLModule.forRoot<ApolloDriverConfig>({
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
driver: ApolloDriver,
playground: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
// /src/brand/brand.type.ts
import { Field, ObjectType, Int } from '@nestjs/graphql';
@ObjectType()
export class Brand {
@Field(type => Int)
id: number;
@Field()
name: string;
@Field()
company: string;
}
// /src/brand/brand.service.ts
import { Injectable } from '@nestjs/common';
import { Brand } from './brand.type';
@Injectable()
export class BrandService {
private brands: Brand[] = [
{ id: 1, name: 'Air Force', company: 'nike' },
{ id: 2, name: 'Jordan', company: 'nike' },
];
findAll(): Brand[] {
return this.brands;
}
findOne(id: number): Brand {
return this.brands.find(brand => brand.id === id);
}
}
// /src/brand/brand.resolver.ts
import { Args, Int, Query, Resolver } from '@nestjs/graphql';
import { Brand } from './brand.type';
import { BrandService } from './brand.service';
@Resolver(of => Brand)
export class BrandResolver {
constructor(private brandService: BrandService) { }
@Query(returns => [Brand])
brands(): Brand[] {
return this.brandService.findAll();
}
@Query(returns => Brand)
brand(@Args('id', { type: () => Int }) id: number): Brand {
return this.brandService.findOne(id);
}
}
brand.resolver.ts
에 정의한 method 를 이용하게 된다. 아래처럼 brands
를 query하면 brand.resolver.ts
의 brands
가 호출된다.
{
brands{
id, name, company
}
}
{
"data": {
"brands": [
{
"id": 1,
"name": "John Doe",
"company": "john.doe@example.com"
},
{
"id": 2,
"name": "Jane Doe",
"company": "jane.doe@example.com"
}
]
}
}
srt / smi / 자막번역 / 무료번역 / srt 번역
deepl 을 사용해서 자막을 번역해준다.
DeeplTranslator
은 우리가 web 에서 사용하는 무료 버전을 사용해서 번역해준다. 코드를 확인해 볼 만 하다.
npm init -y
npm i @remix-run/node @remix-run/react @remix-run/serve isbot@4 react react-dom
npm i -D @remix-run/dev vite
vite.config.js
생성// from : https://remix.run/docs/en/main/start/quickstart#vite-config
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [remix()],
});
app/root.jsx
생성npx remix vite:build
: <proj_root>\build
에 client, server 가 만들어짐.npx remix-serve build/server/index.js
: package.json
의 type: module
이 필요.package.json
{
"name": "skybridge",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"build": "remix vite:build",
"serve": "remix-serve build/server/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@remix-run/node": "^2.9.2",
"@remix-run/react": "^2.9.2",
"@remix-run/serve": "^2.9.2",
"isbot": "^4.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@remix-run/dev": "^2.9.2",
"vite": "^5.3.1"
}
}
모든 route 은 links function 을 export 할 수 있다.
다음과 같은 것으로 보면 된다. <link rel="stylesheet" href="./app.css?url">
import type { LinksFunction } from "@remix-run/node";
import appStylesHref from "./app.css?url";
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: appStylesHref },
];
만약 다음과 같은 file 을 만든다면, 브라우저에서 /contacts/<id>
로 갈 때 contact.$contactId.tsx
파일을 호출할 것이다.
/app/routes/contact.$contactId.tsx
templates, stacks 를 이용하면 쉽게 초기 세팅을 끝낼 수 있다.
quick start 에서 처럼 한번 해본 이후엔 그냥 template을 쓰면 된다.
npx create-remix@latest <my_app_name> –template remix-run/remix/templates/remix
entry.{client|server}.tsx
이 파일은 optional 이다. 이 파일이 있다면, 먼저 browser 는 entry.client.tsx
를 호출하고, 그 후 /app/routes/_index.tsx
를 실행한다.
entry.{client,server}.tsx
optional by mcansh · Pull Request #4600 · remix-run/remixentry.server
and entry.client
files optional · remix-run/remix · Discussion #4601root.tsx
에 Layout
function 이 있으면 그것이 기본적인 Layout 으로 사용된다.
_index.tsx
보통 아래처럼 default function 을 지정하는데, Index
는 다른 이름으로 바꿔도 문제되지 않는다.
...
export default function Index() {
return (
<div></div>
)
}
app/root.tsx
의 Layout
app/root.tsx
의 App
app/routes/_index.tsx
cross platform / cordova
ionic start --list
: template list 를 볼 수 있다.ionic start myApp tabs --type=react
: myApp 이란 이름으로, tabs 라는 template을 사용해서 project생성npm install -g @ionic/cli
ionic start --list
ionic start myApp tabs --type=react
ionic capacitor
ionic 은 기본적으로 Capacitor 를 사용한다.(Capacitor: Everything You've Ever Wanted to Know - Ionic Blog)
ionic serve
: browser에서 app 을 보여준다.ionic capacitor add android
: Capacitor 를 이용해서 android app 추가ionic capacitor build android
: ionic capacitor build | Ionic Documentationionic capacitor open android
: android project를 android studio 에서 열어준다.emulator 를 띄워놓고, ionic capacitor run android --target=Pixel_3a_API_34
를 하면, emulator 에 apk 가 설치되고 실행된다.
환경변수 설정
JAVA_HOME
은 android build 시점에 필요하다.set JAVA_HOME=d:\a\apps\java\jdk-17.0.11.9-hotspot
set ANDROID_HOME=d:\a\appss\Android\Sdk
emulator list : \Android\Sdk\emulator\emulator.exe -list-avd
ionic capacitor run android --list
을 사용할 수도 있다. bat ionic capacitor run android --list
run emulator: \Android\Sdk\emulator\emulator.exe -avd Pixel_3a_API_34
ionic capacitor run android --traget=<target>
ionic capacitor open android
를 실행하면, <proj_root>\android
를 android studio 에서 열어준다. android studio 에서 ‘build apk’ 를 하면 apk 를 얻을 수 있다.
<proj_root>\android\app\build\outputs\apk\debug\app-debug.apk
apache cordova / cross platform /
npm i --save cordova
cordova create mytestapp com.mysite.myapp MyTestApp --template https://github.com/apache/cordova-app-hello-world
cd mytestapp
cordova platform ls
cordova platform add android
cordova build android
cordova create
cordova create <path> <id> <name>
cordova create mytestapp com.mysite.myapp MyTestApp
cordova.cmd create mytestapp com.mysite.myapp MyTestApp --template cordova-template-ngx-onsenui
cordova build
cordova build android
set PATH=d:\a\apps\gradle\gradle-7.5.1\bin;%path%
set JAVA_HOME=d:\a\apps\java\jdk-17.0.11.9-hotspot
set ANDROID_HOME=d:\a\appss\Android\Sdk
d:\a\prog\cordova\node_modules\.bin\cordova.cmd build android
Could not get resource
테스트를 한 환경의 wifi 가 stable 하지 않았는데, 그래서 다음처럼 간혹 Could not get resource
에러가 떴다. 이럴 때는 그냥 다시 build 를 시도하면 된다. 그러면, download 가 잘 이뤄져서 이 이슈는 넘어간다. 이런 식으로 Could not get resource
가 뜨는 경우는 계속 재시도 하면 된다.
* What went wrong:
Execution failed for task ':CordovaLib:compileDebugJavaWithJavac'.
> Could not resolve all files for configuration ':CordovaLib:debugCompileClasspath'.
> Failed to transform kotlin-stdlib-1.7.10.jar (org.jetbrains.kotlin:kotlin-stdlib:1.7.10) to match attributes {artifactType=android-classes-jar, org.gradle.category=library, org.gradle.libraryelements=jar, org.gradle.status=release, org.gradle.usage=java-api}.
> Could not download kotlin-stdlib-1.7.10.jar (org.jetbrains.kotlin:kotlin-stdlib:1.7.10)
> Could not get resource 'https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.10/kotlin-stdlib-1.7.10.jar'.
> Tag mismatch
다음 path 에서 결과물을 확인할 수 있다.
<proj_root>\platforms\android\app\build\outputs\apk\debug\app-debug.apk
Android Virtual Device(AVD) 에서 cordova 로 만든 apk 를 실행할 수 있다.
일단 먼저 AVD 를 띄워놓고, cordova run --emulator
를 실행하면 된다.
android studio 에 가서 avd manager 에서 띄워도 되고, emulator
실행파일을 이용해서 띄워도 된다.
<android_sdk_root>\emulator\emulator.exe -list-avds
<android_sdk_root>\emulator\emulator.exe -avd Pixel_3a_API_34
spring transaction / transactional / 스프링 / spring boot
@Transactional
이 동작하는지 확인하는 방법spring 에서 @Transactional
를 사용하려면, public method 에 annotation 을 달아야 한다. 정확히는 이 @Transactional
이 붙어있는 method 를 현재 @Bean 말고, 다른 Bean 에서 호출해줘야 한다.
@Transactional
동작하는지 확인하는 방법TransactionAspectSupport.currentTransactionStatus
import org.springframework.transaction.interceptor.TransactionAspectSupport;
...
status = TransactionAspectSupport.currentTransactionStatus();
NoTransactionException
을 던지게 된다.TransactionInterceptor.invoke
: call stack 에 TransactionInterceptor.invoke
가 보인다.다른 Bean 에서 호출하지 않고, 현재 @Bean 내의 method 에서 @Transactional method 를 호출하는 것으로는 bean 을 생성하지 않는다. 그러니까 spring 이 처음 run 할 때 @Transactional
에 대한 proxy class 를 만드는데, 그 작업을 하지 않는 것 같다.
cglib 를 호출할 때의 call stack:
- SpringNamingPolicy.getClassName(String,String,Object,Predicate) (\spring-core-6.0.7.jar\org.springframework.cglib.core\SpringNamingPolicy.class:41)
- AbstractClassGenerator.generateClassName(Predicate) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:169)
- AbstractClassGenerator.generate(AbstractClassGenerator$ClassLoaderData) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:339)
- Enhancer.generate(AbstractClassGenerator$ClassLoaderData) (\spring-core-6.0.7.jar\org.springframework.cglib.proxy\Enhancer.class:575)
- AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:103)
- 0x0000000800f7bf70.apply(Object) (Unknown Source:-1)
- LoadingCache.lambda$createEntry$1(Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core.internal\LoadingCache.class:52)
- 0x0000000800f7c5c0.call() (Unknown Source:-1)
- FutureTask.run() (\java.base\java.util.concurrent\FutureTask.class:264)
- LoadingCache.createEntry(Object,Object,Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core.internal\LoadingCache.class:57)
- LoadingCache.get(Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core.internal\LoadingCache.class:34)
- AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator,boolean) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:126)
- AbstractClassGenerator.create(Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:313)
- Enhancer.createHelper() (\spring-core-6.0.7.jar\org.springframework.cglib.proxy\Enhancer.class:562)
- Enhancer.createClass() (\spring-core-6.0.7.jar\org.springframework.cglib.proxy\Enhancer.class:407)
- ConfigurationClassEnhancer.createClass(Enhancer) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassEnhancer.class:138)
- ConfigurationClassEnhancer.enhance(Class,ClassLoader) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassEnhancer.class:109)
- ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassPostProcessor.class:514)
- ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassPostProcessor.class:304)
- PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(Collection,ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.support\PostProcessorRegistrationDelegate.class:358)
- PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory,List) (\spring-context-6.0.7.jar\org.springframework.context.support\PostProcessorRegistrationDelegate.class:150)
- AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.support\AbstractApplicationContext.class:747)
- AbstractApplicationContext.refresh() (\spring-context-6.0.7.jar\org.springframework.context.support\AbstractApplicationContext.class:565)
- ServletWebServerApplicationContext.refresh() (\spring-boot-3.0.5.jar\org.springframework.boot.web.servlet.context\ServletWebServerApplicationContext.class:146)
- SpringApplication.refresh(ConfigurableApplicationContext) (\spring-boot-3.0.5.jar\org.springframework.boot\SpringApplication.class:732)
- SpringApplication.refreshContext(ConfigurableApplicationContext) (\spring-boot-3.0.5.jar\org.springframework.boot\SpringApplication.class:434)
- SpringApplication.run(String[]) (\spring-boot-3.0.5.jar\org.springframework.boot\SpringApplication.class:310)
- FishApplication.main(String[]) (d:\a\prog\foodpang\purchase\fish\fish\src\main\java\co\foodpang\fish\FishApplication.java:24)
- NativeMethodAccessorImpl.invoke0(Method,Object,Object[])[native method] (\java.base\jdk.internal.reflect\NativeMethodAccessorImpl.class:-1)
- NativeMethodAccessorImpl.invoke(Object,Object[]) (\java.base\jdk.internal.reflect\NativeMethodAccessorImpl.class:77)
- DelegatingMethodAccessorImpl.invoke(Object,Object[]) (\java.base\jdk.internal.reflect\DelegatingMethodAccessorImpl.class:43)
- Method.invoke(Object,Object[]) (\java.base\java.lang.reflect\Method.class:568)
- RestartLauncher.run() (\spring-boot-devtools-3.0.5.jar\org.springframework.boot.devtools.restart\RestartLauncher.class:49)
외래키 / hibernate / join / spring boot / spring / java / orm / 연결 / 관계 설정 / relation entity
user table 의 profile_id 가 profile table 의 id 를 참조하도록 하는 예시이다.
아래 code example 이 있다.
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id", referencedColumnName = "id", nullable=false)
private Profile profile;
...
}
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import java.time.LocalDateTime;
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "profile")
private User user;
create table user (id bigint not null auto_increment, profile_id bigint not null, primary key (id)) engine=InnoDB;
create table profile (id bigint not null, primary key (id)) engine=InnoDB;
alter table user add constraint FKl6pykqur53o90qdnhjko0r8ni foreign key (profile_id) references profile (id);
어느 값이 어느값을 가리키는지는 아래 그림을 참고하자.
CQRS (Command Query Responsibility Segregation) 의 핵심은 read/write 가 구분된다는 점이다.
보통 우리가 client 라고 부르는 쪽에서 필요한 데이터를 가져올때(read) 와 변경사항을 저장할 때(write) 우리는 같은 model 을 사용한다.
그런데 CQRS 는 이것을 각자 다른 model 을 사용한다. 한 예는 read때는 read.myserver.com 을 사용하고, write 때는 write.myserver.com 을 사용한다고 봐도 된다.
간략하게 어떤 개념인지를 파악하는데는 마틴파울러의 글이 좋은 듯 싶다. 글에 있는 이미지도 이해를 하는데 도움이 된다.
CQRS pattern - Azure Architecture Center | Microsoft Learn 의 내용을 일부정리
CQRS 에선 command 는 update 할 때 query 를 read 할 때 쓰인다. 그리고 write 과 read 에 대해서 각자 다른 model들을 사용한다.
data 를 update 할 때는 command
data 를 read 할 때는 query
command 는 task based 여야 한다. data centric 이 아니라.
command들은 synchronous 하게 처리되지 않고, queue 에 넣어서 asynchronous 하게 처리할 수 있다.
Query 들은 db를 변경하지 않는다. query는 도메인 지식을 캡슐화하지 않는 DTO를 반환한다.
이렇게 write 와 read 에 대한 model 을 분리하면, 설계(design), 구현(implementation)이 간단해진다.
isolation 을 강화하기 위해 물리적으로 read용, write용으로 db를 나눠도 된다. read 의 경우 필요한 join 등을 미리 해놓은 결과를 저장해서 보여주는 것처럼 다른 schema 를 사용할 수 있다. 이것은 materialized view 를 쓰거나, NoSQL 같은 다른 type 의 db 를 쓰는 등의 방법을 사용할 수 있다.
이 경우 write 에 사용하는 db 와 read 에 사용하는 db 의 동기화가 필요한데, 이때는 대체로 write db 에 update 가 발생할 때 event 를 발생시켜서 read db 를 update 하게 된다. 다만 이때의 문제는 db에 write 가 된 이후에 event 가 성공적으로 전달되지 못하는 경우 data 의 consistency 를 보장할 수 없다.
read store 와 write store read store 는 write store 의 복제본(read-only replica)이거나, write store 와 상관없이 다른 구조를 갖고 있을 수도 있다. - 여러개의 read-only replica 를 갖고 있으면, 이 replica 가 application instance 에 가까이 있는 경우, query 성능이 향상 될 것이다. - 보통 read store 의 부하가 더 높은데, read 와 write store 를 분리하면, 각각의 부하에 맞게 적절하게 확장이 가능하다.
CQRS 일부 구현은 event sourcing pattern 을 사용한다.
구현상 어려운점
언제 CQRS pattern 을 쓰는가?
쓰지 않는게 나은 경우
CQRS pattern 을 시스템의 제한된 부분(이 부분이 추후 중요해질 것이라 여겨지는)에 적용하는 것을 고려해보자.
얼굴 포토샵 감지 / 감별 / 확인 방법 / 뽀샵
FAL detector, Face-Aware Liquify detector
Adobe Research와 UC Berkley에서 개발한 AI 기술인 FAL Detector는 포토샵의 Face-Aware Liquify’ 기능을 이용하여 왜곡된 이미지를 학습