2024년 11월 6일(제 15회 전국 마이스터고등학교 제전 영마스터 학술제) 부스 출품을 위해 대전으로 가는 버스 안에서 창밖을 보고 멍때리다가 어느 간판을 봤다. 그 간판을 보고 저 간판을 이용해서 어떤 아이디어를 생각해보자고 의식적으로 생각했다. 뭔 말인지 모르겠다면 그냥 패스하도록 하자.
어쨌든 그렇게 생각한 아이디어는 세상의 모든 것을 줌인 줌아웃으로 표현하는 것이다. 아니 상상에 더 가깝다. 한번 상상해봐라! 지구가 있다. 손가락으로 줌을 한다. 어디로 갈까? 내가 사는 한반도의 남쪽 지역에 가보자. 계속해서 줌인을해서 내가 살고 있는 지역에 더 가까운 위치에 있는 열정과 자유의 도시…. 파워풀 대구로 가보자. 흠 저기 팔공산도 보이고 동성로도 있고 동대구역도 있고 신천도 있고 이월드도 있다. 쭉쭉 계속해보자. 이번엔 장발 생머리의 젊은 미모의 여성이 동대구역 버스정류장에서 20분째 지연된 814번 버스를 기다린 채 짜증을 내고 있다. 이런, 우리의 목적은 미모의 여성이 아니다. 그냥 줌인 줌아웃을 하는 것이다. 조금 갑작스럽겠지만 이제 우리는 이 사람 내부를 탐방해야 한다. 상상속 미모의 여성에게는 미리 사과의 말씀을 전해야겠다. 이 사람의 머리 속을 줌인 해보자. 머리 안에는 무엇이 있을까? 두개골? 눈? 뇌? 신경? 본인이 궁금한 걸 선택해서 줌인 하는 상상을 해보자. 궁금한 게 없을지도 모른다. 하지만 아무래도 상관없다. 우리는 줌인을 너무 많이 해버린 나머지 어느새 분자 레벨까지 도착해버렸다. 분자 안에는 또 어떤 게 있을까? 아니 그 전에, 이 뇌 속 작은 세상 속에는 어떤 분자들이 존재하고 있을까? 그 분자는 또 어떻게 구성되어 있을까? 흠 수많은 궁금증이 생기지만 일단 좀 더 줌인을 해보자. 아직 많이 남았다. 원자다. 흠 생각보다 따분한 것 같다. 역시 혼자는 외로운 것 같다. 또 한 번 줌인을 하면 양성자, 중성자 주위를 돌고 있는 정확하게는 확률적으로 존재하는 전자가 보인다. 흠, 어떤 똑똑한 혹자는 여기서부터 불확정성의 원리 같은 어려운 개념을 들고 와서 나의 상상속 줌인 세상의 실현 가능성에 대해 반론을 제기할지도 모르겠다. 그러나 여기는 상상속의 세상! 내 맘대로 전자를 보고 양성자도 볼 것이다. 계속해서 쿼크까지 봐보자. 어머, 쿼크는 깜짝 놀란다. 쿼크는 우주가 탄생한 138억년동안 본인이 이렇게까지 확대 당할 수 있을지 몰랐을 것이다. 인간 주제에 감히 나를 관측하다니…
반대의 방법으로 줌아웃을 해보자. 내가 보는 세상, 그 넘어엔 어떤 놀라운 것들이 있을까? 지구, 화성, 태양, 오르트구름이 보인다. 우주는 너무 넓어서 내가 찾으려는 것들을 찾기 너무 어려운 것 같다. 하지만 여기는 상상 속의 줌아웃 세상! 모든 것들이 인간이 보고 이해하기 쉽도록 크기가 조정되어있다. 덕분에 빠르게 내가 찾고 싶었던 바로 블랙홀을 찾을 수 있었다. 흠… 줌인을 했다간 큰일 날 수도 있을 것 같으니 사건의 지평선 까지만 보고 블랙홀과는 작별인사를 하도록 하자. 사건의 지평선에는 어떤 게 있을까? 여기서는 어떤 신기한 현상이 발생할까? 사건의 지평선은 전체적으로 봤을 때 어떤 모양일까? 사건의 지평선은 블랙홀의 모양에 따라 바뀌겠지? 아니 잠깐만, 그럼 블랙홀은 어떤 모양일까? 블랙홀에 모양이라는 게 있을까? 수많은 궁금증을 갖고 일단 다시 줌 아웃 여행을 떠나보도록 하자. 계속해서 줌 아웃을 하면 우주의 끝에 도달할 수 있을까? 괜찮다 여기는 상상속 세상이라 줌 아웃을 하는 속도가 빛의 속도보다 빠르다. 정말 도착했다. 우주의 끝에. 평행 우주는 있을까? 만약 있다면 어떤 모양일까? 거기도 시간의 개념이 있어 세상이 흐르는 것처럼 보일까? 물리법칙이 같을까?
조금 더 현실적인 이야기를 해보자. 나는 컴퓨터를 잘 아니까 컴퓨터 말고 내가 모르는 분야에 대해 상상해볼 것이다. 나는 자동차 내부가 어떻게 생겼는지 궁금하다. 흠 그 중에서 테슬라 같은 전기차를 상상해볼 것이다. 이왕이면 정열의 빨간색으로. 한번 껍데기를 다 뜯어보자. 자동차에 대해 무지한 나는 이 외관, 표면을 껍데기로 부를 것이. 또, 내 눈에 이 껍데기는 그냥 철판때기로 밖에 보이지 않는다. 어쨌든 껍데기를 벗기니 훨씬 더 단단한 철 프레임이 있을 거 같다. 그리고 배터리랑 모터랑 그걸 연결하는 선들도 좀 있을 거 같고 센서도 엄청 많이 있을 거 같다. 가만보니 나는 모터에 대해 그냥 단순히‘돌아가게 하는 것’으로 알고 있는 것 같다. 모터가 어떻게 동작하는지 궁금하다. 모터를 줌인 해보자. 하.. 이제부터 상상하기 너무 힘들다. 나의 지식에 한계가 온 것 같다. 뭔가 코일이 있을 거 같고 기하학적이고 공학적으로 이루어진 단단하고 세밀하고 작은 무언가가 여러가지 모여서 구조를 형성하고 있을 것 같다. (라고 밖에 상상을 못하겠다.) 어쨌든 여기서 계속해서 줌인을 하면 결국에는 또 다시 쿼크를 만나 또 깜짝 놀래킬 것이다.
이것은 단순히 ‘인간이 세상에 대해 더욱 깊이 이해할 수 있는 계기’를 넘어 디지털 트렌스포메이션과 온톨로지의 완성이자 가상 세계의 구현일 것이며 전지전능한 신에 대항하는 권위적인 도전이 될지도 모른다고 감히 말하겠다.
ChatGPT가 문자 기반 정보 전달 방식에서 혁신을 이뤄냈듯, 이런 방식은 관측 기반 정보 전달 방식에 혁신을 불러일으킬 것이다.
너무 과장한 거 아니냐고 물을 수도 있다. 그러나 한번 상상해보아라. 첫 번째로, 인간의 눈 넘어의 것들을 볼 수 있는 구글 어스, 현미경, 갤럭시 S25 카메라 같은 발명품들을 한대모아 세상을 본 뜬다. 다음으로 인간의 해석을 더한다. 각 존재의 정의와 존재 사이의 관계를 정의한다. 왜냐하면 인간의 해석이 없다면 인간에게 아무런 의미가 없기 때문이다. 마지막으로 더 많은 인간의 해석을 더한다. 현실의 데이터를 분석한 결과를 넣는 것이다. 날씨, 교통 등등… 모든 인간에게 이익되는 데이터들은 다 해당한다.
자 이제, 구현할 시간이다. 이걸 어떻게 구현하나?
이 글의 목적이 Swift Student Challenge(a.k.a 스스챌)에 출품한 앱의 개발 과정이라는 사실을 잊지마라. 스스챌은 25MB 용량 제한이 있어 그 용량 안에 앱을 잘 구겨 넣어야 하고 심지어 앱을 로컬에서 빌드, 동작할 수 있어야 한다.
이때 당시에 나는 고등학교 2학년이었다. 얘 머리 속에 든 거는 컴퓨터 공학 지식과 프로그래밍 언어, 프레임워크 같은 남들 다 배우는 하등한 데이터 쪼가리와 약간의 인문학적 지식 뿐이다. 그러나 이 프로젝트에서 필요한 것은 물리학적 지식을 사용하여 세상을 시뮬레이션 하는 것과 3D화면을 렌더링하는 능력이다. 그런 게 내 머리 속에 있을리가 없잖아.
사실 다 개소리다. 고멘나사이. 물리학적 지식 같은 건 필요없다. 되도않는 지금까지 그럴듯한 말들만 주구장창 한 거 같은데 그냥 사실 이쁘게 보여주기만 하면 된다.
쨌든 초기에 구상한 방식은 전체를 그래프 구조로 보는 것이다. 그래프로 하는 이유는 줌인/줌아웃 세상의 각 물체들을 상상을 하면 직관적으로 이해할 수 있다. 그래프의 양 끝은 각각 하나로 환원되는 요소일 것이다. (아마도)쿼크와 우주. 전체 의 모습은 나뭇잎 모양 같을 것이다. 이해가 안 된다면 그냥 건너뛰어도 된다.
근데 이건 너무 어렵다고 판단했다.
두 번째 구조는 트리를 생각했다.
루트 노드는 우주이다. 핵심은 자동차를 줌인 할거면 자동차만 탐색하고 인간을 탐색할거면 인간의 요소만 탐색하자는 것이다. 탐색에 중점을 두자는 것이다.
이 방식은 위의 2번 문제를 해결할 수 있다. 한 요소는 다른 한 요소만 바라보기 때문이다.
그러나 아직 1번 문제가 남아있다. 그리고 쿼크를 만나보기 어렵다는 문제점이 있다. 쿼크는 어느 리프 노드에 존재해야 하는가…
그냥 일직선으로 만들기로 결정했다. 원래는 유저가 줌인 위치를 특정 대상에게 가리키면 그쪽으로 줌인이 되는 유동적인 방식으로 하려고 했다. 근데 너무 어렵다. 무식하게 줌인/줌아웃만 하는 것이다.
2024년 11월 10일 일요일 저녁 스타벅스 대구국가산단 DT점 3층 계단 올라가자마자 있는 책상 오른쪽 맨 끝에서 두 번째 자리에 앉아 Xcode을 켰다. 이 스벅이 내가 가본 스벅 중에서 제일 좋은 거 같다. 사람도 적고 뷰도 좋고 학교랑 멀어서 나랑 동급인 고등학생 엔티티들을 만나보기 어려워서 집중이 잘 된다는 장점이 있다.
문제가 있다. 스스챌은 무조건 Swift언어를 사용해서 앱을 만들어야 한다. 이건 유니티가 아니다. Swift에서 3D를 구현할 때 SceneKit이라는 걸 써야 한다.
ChatGPT한테 SceneKit 좀 가르쳐달라고 했더니 아래처럼 생긴 귀여운 빨간 큐브하나를 만들어줬다.
이후로 저녁까지 풀 집중해서 태양계 하나를 만들었다. 저거 실제로 궤도 따라서 움직인다. 타원은 아니고 원 운동을 한다. 원이 쉬워서 원으로 했다. 어차피 원인지 타원인지 케플러가 아닌 이상 아무도 신경 안 쓴다.
다음으로 태양계의 요소를 클릭하면 해당 요소로 포커스가 되는 기능을 만들었다.
음 저건 줌인/줌아웃이 아니지 않냐고? 태양계는 좀 봐줬다. 단순 줌인/줌아웃만하면 뭔가 지구로 줌인이 되어야할 것만 같은 느낌이 직관적으로 들지 않나. 근데 수성이랑 금성이랑 화성이 궁금하면 어떻게 할까. 곧 있으면 화성이 인류의 식민지가 될 텐데 말이다.
이 애니메이션을 구현하는 데에 SCNTransaction.begin(), SCNTransaction.commit()함수를 사용하는데 내 예상대로 동작하지 않아서 많이 애를 먹었다. 저 코드가 매 프레임마다 실행되는데 도대체 어떻게 동작하는지 지금도 모르겠다. 매 프레임마다 애니메이션을..? 별 문제 없길래 그냥 썼다.
2024년 11월 12일 화요일 이후로 잠적의 시간을 가졌다. 사실 귀찮은 것도 있었고 여러모로 다른 할 게 많았다.
프로젝트 실습이라는 과목이 있는데 React로 코드스테이지라는 코드리뷰 플랫폼을 만들어야 했다. (사실 거의 해커톤급 수준으로 던져버림) 또 다른 프로젝트에서는 링크메리라는 모바일 청첩장 서비스 MVP를 개발해야 했다. 이것도 React다.
사실 다 구라고 걍 개발하기 싫었다. 애니봤다. 강철의 연금술사보고 데스노트보고 귀멸의 칼날 보고 워킹데드 봤다. 아마 개발이 내 인생에 도움이 되는지 현실적인 고민을 했었던 거 같다. 대회나 해커톤 나가고 장학금 타는 거 제외하고 순수 프로덕트로 번 돈은 0원 이었다. 아 외주가 있구나. 그니까 안정적인 고정 수입 말이다. 또 기술, 아키텍처를 적용해보고 새로운 개념을 공부해보고 하는 게 너무 질렸다.
이러니까 어쩔 수 없이 애니를 볼 수 밖에 없었다. 라고 하기엔 너무 재미있게 봤긴한데. 쨌든
2025년 1월 31일 다시 프로젝트를 진행하기로 마음 먹었다. 사실 이때도 겁나 바빴다.
Pulzze Systems라는 회사에서 온라인 인턴쉽을 해야 했다. 새벽 5시까지 백엔드 친구랑 소켓 통신하느라 밤을 샜다. 링크메리는 MVP 릴리스 이후 서비스를 완전 개선하기로 해서 사실상 v2를 만들어야 했고 교내에서 쓰는 도담도담 서비스가 있는데 이 프로젝트에서는 그룹, 공지, 학부모 같은 신규 피쳐를 개발해야 했다. 내가 디자인을 좀 잘 해 버리는 바람에 세 프로젝트에 전부 프론트를 포함해 디자인까지 했다. 이 상황에서 스스챌은 완전한 사치였고 개인주의였다.
뭐 이미 시작했는데 어쩔 수 있나. 방학 때 방구석에 박혀서 코딩만 할 각오로 진행 하기로 했다.
흠.. 스스챌 제출이 2월 3일부터 3주간이라 사실상 3주만에 개발을 끝내야 했다. 근데 내가 봤을 때 지금 이 구조로는 절대 3주만에 개발 못 한다. 시간 지나고 다시보니 애니메이션도 구리고 화면 전화할 때 잠깐 깜빡하고 생기는 흰색 화면도 이상하다. 그냥 처음부터 다시 만들기로 했다.
근처 카페에 가서 고찰의 시간을 가졌다.
뭔가 기획부터 잘못된 거 같다. 지난 번 개발 기간에는 너무 구현에만 초점을 맞추었다. 생각을 하자 생각을.
3D 모델을 찾는 게 너무 힘들었다. 리소스들이 25MB안에 전부 들어가야 하는데 모델이 10개가 넘는데다가 모델뿐만이 아니라 이미지까지 리소스에 포함이었기 때문이다. 그럼 각 모델의 용량은 최대 2MB 이내 여야 한다. 나는 용량이 작으면서 퀄리티는 높은 모델을 원했다. 용량 제한이 있고 로컬에서만 동작해야 한다는 조건이 너무 가혹했다.
요소를 클릭하면 해당 요소에 대한 설명을 볼 수 있다. 이 내용은 전부 ChatGPT를 사용해서 생성했다. 원래 위키백과 같은 나름 신빙성 있는 곳에서 하나하나 찾아서 정리하려고 했지만 시간이 없었다.
SceneKit과 SwiftUI는 잘 맞지 않는 것 같다. SceneKit은 UIKit의 코드스타일과 비슷하다.
언제는 메모리 누수가 발생하기도 했다. SCNNode의 clone함수를 적절히 사용해서 메모리 누수를 방지할 수 있었다.
import SwiftUI
import SceneKit
struct NormalDetailView: View {
// ...
var body: some View {
ScaleModeDetailView(
node: node.clone()
)
// ...
}
}
SceneKit 특성상 setter하는 부분이 많다. 그래서 apply, let, set과 같은 Scope함수를 만들어 코드 신택스를 더욱 유연하게 만들었다. 코드를 길게 작성하지 않아도 됐다. 자세한 ScopeKit
let neutrons = SCNNode().apply {
$0.setAllName(.neutrons)
$0.addTitle().let {
$0.position.y -= 10
$0.position.x += 10
}
$0.addChildNodes(
createNucleon(color: .red).apply {
$0.position.z += 0.4
},
createNucleon(color: .red).apply {
$0.position.x += 1.5
$0.position.y += 0.8
},
createNucleon(color: .red).apply {
$0.position.y -= 1.8
},
createNucleon(color: .red).apply {
$0.position.x -= 1.5
$0.position.y += 0.8
},
// indicator
SCNNode().apply {
$0.geometry = SCNTube(innerRadius: 0, outerRadius: 0.1, height: 5).apply {
$0.firstMaterial?.diffuse.contents = UIColor.red
}
$0.eulerAngles = SCNVector3(0, 0, .angle(65))
$0.position.x += 2.5
$0.position.y -= 3
}
)
}
요소를 클릭하면 세부 페이지로 이동하도록 구상했다. 그래서 요소를 클릭하는 부분을 구현해야 했는데 문제는 코드 스타일이 너무 구리다는 것. (마치 Android XML에서 Fragment에 OnClickListener를 상속 받아서 onClick메서드를 override해서 핸들링하는 느낌.)
클릭 이벤트를 감지하는 부분도 코드 스타일이 마음에 들지 않아 직접 구현했다.
나도 이게 맞는 방식인지는 잘 모르겠다. 아마 절대 아닐 것이다. objc_getAssociatedObject라는 이상한 걸 썼다. 나 주제에 감히 클릭을 구현하려 들다니. 나보다 더 잘 하는 프로그래머가 만든 구현을 갖다 쓰는 게 더 안전한 방법일 것이다. 그러나 아무리 찾아도 내가 원하는 방식의 SceneKit 클릭 관련 자료를 찾을 수 없었다.
아래 코드는 클릭을 구현한 코드의 일부이다. 자세한 구현은 GestureHandler.swift와 SCNNodeExt.swift를 참고하자.
extension SCNNode {
private struct AssociatedKeys {
static var actionKey = "actionKey"
static var isClickingKey = "isClickingKey"
}
private var action: (() -> Void)? {
get {
objc_getAssociatedObject(self, &AssociatedKeys.actionKey) as? (() -> Void)
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.actionKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
private var isClicking: Bool {
get {
objc_getAssociatedObject(self, &AssociatedKeys.isClickingKey) as? Bool ?? false
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.isClickingKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func addAction(_ action: @escaping () -> Void) {
self.action = action
self.childNodes.forEach { node in
node.addAction(action)
}
}
func clickDown() {
guard action != nil, name != nil else { return }
self.isClicking = true
self.opacity = 0.4
self.childNodes.forEach { node in
node.clickDown()
}
}
func clickUp(clicked: Bool = false) {
guard name != nil else { return }
self.opacity = 1
var clicked = clicked
if isClicking && !clicked {
self.action?()
clicked = true
}
self.isClicking = false
self.childNodes.forEach { node in
node.clickUp(clicked: clicked)
}
}
func cancelClicking() {
guard name != nil else { return }
self.opacity = 1
self.isClicking = false
self.childNodes.forEach { node in
node.cancelClicking()
}
}
}
@objc func handleLongPressGesture(_ gesture: UILongPressGestureRecognizer) {
let location = gesture.location(in: parent.scnView)
let hitResults = parent.scnView.hitTest(location, options: [.searchMode: SCNHitTestSearchMode.any.rawValue])
let hitResult = hitResults.first
let hitNode = hitResult?.node
if [.ended, .cancelled].contains(gesture.state) {
// 3 Depth 까지 탐색 <- 이거 야매 방식임. 진짜 삽질하다가 그냥 겨우 이 방식으로 구현하기로 함... 모든 노드를 재귀적으로 찾으면 특정 모델의 요소들이 서로 순환 참조되는 문제가 생겨서 어쩔 수가 없었음.
parent.scnView.scene?.rootNode.childNodes.flatMap { [$0] + $0.childNodes.flatMap { [$0] + $0.childNodes } }
.forEach { node in
if hitNode?.name == node.name {
node.clickUp()
} else {
node.cancelClicking()
}
}
}
if gesture.state == .began {
hitNode?.clickDown()
} else if [.ended, .cancelled].contains(gesture.state) {
hitNode?.clickUp()
}
self.parent.scnView.setNeedsDisplay()
}
init() {
node.addAction {
// TODO: implementation
}
}
우주를 큐레이팅 해주는 AI 어시스턴스를 만들고 싶었다. 그러나 25MB라는 작은 공간에 로컬에서 동작하는 LLM을 넣는 건 불가능했다. Swift에 관련 프레임워크가 있는지 찾아보았으나 없었다. 그래서 생각한 방식이 바로 질문과 답변을 미리 지정해놓고 사용자가 제시된 3개의 질문 중 원하는 질문을 선택하면 답변을 보여주는 식으로 구성하였다. UI는 마치 ChatGPT를 연상캐하도록 하여 실제로 어시스턴트와 대화하는 느낌을 받도록 만들었다.
문장를 Char단위로 쪼개서 Text 뷰로 만든다음 FlowLayout으로 감싸서 구현했다.
FlowLayout {
ForEach(Array(displayedContent.enumerated()), id: \.0) { _, char in
CharacterView(
character: char,
sender: message.sender
)
}
}
타이머를 사용하여 0.015초 동안 스케줄링을 하여 다음 글자를 보여주는 방식으로 구현했다. 또, 그 글자가 나타날때 fade in 애니메이션을 적용해서 부드럽게 나타나도록 했다.
또, 어시스턴트의 채팅뷰 특성상 스크롤 방향이 반대여야 한다. 이건 예전에 구현한 적이 있어서 빠르게 구현할 수 있었다. (CustomScrollView.swift 참고)
원통형 모양으로 만들어 맵을 감싸는 형태로 배경을 만들었고 작은 구를 흩뿌리는 방식으로 파티클도 추가했다. 또, 사용자가 앱에 더욱 몰입하여 탐색할 수 있도록 우주적 분위기에 맞는 음악을 추가했다. 설정에서 음량을 조절할 수 있도록 만들었다. 앱 로고는 ChatGPT로 생성해서 만들었다.
원래는 아래처럼 변수를 선언하여 사용하였다. cameraPosZ는 줌인 정도를 나타내는 변수이다.
@State private var cameraPosZ: Float = 0
그러나 디테일뷰로 이동했다가 다시 dismiss하는 경우 변수가 초기화 되어 카메라 위치가 기본 위치로 초기화 되는 이슈가 생겼다.
그래서 @StateObject를 사용해서 뷰가 없어져도 상태를 유지하도록 만들려고 했다. 근데 iOS 18부터인가 @StateObject + ObservableObject조합에서 @State + @Observable조합으로 변경 되었다고 한다. 작동 방식에는 별 차이가 없는 거 같다. 이슈는 해결되었고 예상대로 동작했다.
import SwiftUI
@Observable
final class ScaleModeViewModel {
var cameraPosZ: Float = 0
}
이런, 전체 파일 용량이 35MB를 넘어섰다. 이걸 이제 25MB로 압축시켜야 한다.
먼저, 잘 보니 안 쓰는 이미지가 있어서 삭제하였다. 32MB 정도가 되었다. 그리고 2~3MB에 달하는 큰 용량의 이미지들을 리사이징 해주는 웹사이트를 사용해서 1MB안으로 압축시켰다. 같은 사진을 2~3번 반복해서 압축한 거 같다. 다행히 25MB 안으로 억지로 구겨넣을 수 있었다. 그러나 배경 이미지의 퀄리티가 낮아지는 문제가 생겼다. 그래서 배경이미지는 1번만 압축시키고 퀄리티가 낮아도 상관없는 행성이나 텍스쳐 사진을 더 압축시켰다.
스스챌은 앱이랑 그 앱의 설명 모두 영어로 제작해야 한다. 앱 설명은 대략적인 개요만 프롬프트로 입력해서 ChatGPT로 생성하도록 했다.
모든 정보를 다 입력한 뒤 제출을 하려고 했다. 근데 Submit 버튼이 나타나지 않는 버그가 있었다. 노션으로 입력 값들을 전부 복사하고 새로고침 하고 다시 입력했다… 애플 사이트에 이런 버그가 있다니 충격이었다. 버그가 아니더라도 유저에게 피드백을 주는 UX가 있었으면 좋았을 텐데
원래 구상했던 것보터 한참 퀄리티가 낮아서 사실 좀 실망했다. 우주를 전체적으로 봤을 땐 괜찮은 거 같은데 각 요소에 대한 부가 기능이 좀 부족한 거 같았고 콘텐츠도 별로 없는 거 같았다. 그래서 내가 정말 우승할 수 있을지 의문이 들었다. 심사위원이 완성도가 낮다고 탈락시킬 것 같았다.
그래도 1달 동안 기다린 끝에…
우승!!!! 이야 미국간다!!!! AirPods Max!! 애플 연회비 패스권까지!!
시연 영상: https://youtu.be/HEaI9YtJryI
깃허브: https://github.com/hhhello0507/Zoom-In-Universe
존경합니다