Cyluma
Product Hunt
Your Mac’s battery as a living, breathing landscape Discussion | Link
Score: 51.52Confidence: 46%
View offerLoading the catalog…
THE AI OPPORTUNITY INDEX
Find your next AI tool. Explore free access, trials, and credits — all in one place.
Product Hunt
Your Mac’s battery as a living, breathing landscape Discussion | Link
Score: 51.52Confidence: 46%
View offerhacker-news-frontpage
169 points · 13 comments · by eustoria
Score: 51.28Confidence: 46%
View offerOpenAI
Ensuring that AI systems are built, deployed, and used safely is critical to our mission.
Score: 37.4Confidence: 54%
View offerAIタグが付けられた新着記事 - Qiita
TOAI6 Report: Visual Noise Reduction and Asset Cleanup This is an automated report generated by the TOAI High-Precision Executor. Overv...
Score: 57.36Confidence: 54%
View offerAIタグが付けられた新着記事 - Qiita
はじめに 2026年10月1日、Google 公式ブログに「Gemini 4 Argon」と題した記事が公開されました。Hacker News では 1,581 ポイントを集め、コミュニティの関心は非常に高い状態です。 ⚠️ この記事の前提 今回入手できた情報は、タイト...
Score: 57.35Confidence: 54%
View offerReadhub
英国央行指出对冲基金在英国国债市场的高杠杆水平,叠加其对人工智能相关资产和企业债务的风险敞口,加剧了市场压力相互传导的风险,令市场更易受到同时爆发的压力冲击。当前英国 10 年期国债收益率已逼近 2008 年金融危机期间的水平,金融政策委员会正研究提升国债回购市场韧性、管控相关风险的措施,以应对过去 18 个月市场杠杆率上升带来的风险,相关措施预计 2027 年初发布。
Score: 57.17Confidence: 54%
View offervelog
BE 25일차 - Microservice와 Spring Cloud 1. 오늘의 핵심 오늘은 Monolithic Arc hitecture와 Microservice Architecture의 차이를 비교하고, 여러 Microservice를 운영하기 위해 필요한 Spring Cloud의 구성요소를 학습했다. Monolithic 하나의 애플리케이션 안에 모든 기능을 구성 ↓ 일부 기능을 수정해도 전체 애플리케이션을 다시 빌드·배포 Microservice Architecture 비즈니스 기능을 독립된 서비스로 분리 ↓ 변경된 서비스만 빌드·테스트·배포·확장 가능 MSA는 단순히 Spring Boot 프로젝트를 여러 개 만드는 것이 아니다. 서비스가 분리되면서 생기는 주소 관리, 설정 관리, 서비스 간 통신, 장애 전파, 데이터 일관성, 모니터링 문제까지 함께 해결해야 한다. 오늘 학습한 전체 구조는 다음과 같다. Client ↓ 하나의 진입점 API Gateway ├─ Service Discovery에서 서비스 위치 조회 ├─ User Service ├─ Order Service └─ Catalog Service 공통 설정 : Config Server 동기 통신 : REST API 비동기 통신 : Kafka · RabbitMQ 실행·격리 : Container 배포 자동화 : CI/CD 관측 : Log · Metric · Trace 2. Software Architecture란? Software Architecture는 소프트웨어를 구성하는 요소와 요소 사이의 관계를 정의한 전체 구조다. 구성요소 + 구성요소 사이의 관계 = Software Architecture Architecture는 단순히 기능을 구현하는 방법만 설명하지 않는다. 개발자, 설계자, 운영자, 사용자 등 서로 다른 이해관계자가 시스템의 전체 구조와 호출 관계를 함께 이해하도록 돕는 의사소통 도구이기도 하다. 대표적인 관점은 다음과 같다. 관점 확인하는 내용 Logical View 기능과 주요 객체의 구조 Process View 실행 과정과 동시성 Implementation View 코드와 모듈의 구성 Deployment View 서버와 실행 환경의 배치 Use Case View 사용자의 기능 이용 흐름 3. Monolithic Architecture Monolith는 여러 업무 기능을 하나의 애플리케이션으로 패키징하고, 대체로 하나의 배포 단위로 운영하는 구조다. Client ↓ Monolithic Application ├─ User ├─ Blog ├─ Comment └─ Order ↓ Database 예를 들어 사용자 기능만 수정해도 일반적으로 전체 애플리케이션을 다시 빌드하고 배포해야 한다. User 기능 수정 ↓ 전체 Test ↓ 전체 Build ↓ 전체 Application 배포 장점 코드 탐색과 Debugging이 비교적 쉽다. 하나의 프로세스 안에서 호출하므로 Network Latency가 적다. Transaction과 데이터 일관성을 관리하기 쉽다. 작은 서비스에서는 개발과 배포 구조가 단순하다. 단점 기능 사이의 결합도가 커지기 쉽다. 일부 기능만 변경해도 전체를 다시 배포해야 한다. 특정 기능에만 Traffic이 몰려도 전체 애플리케이션을 확장해야 한다. 하나의 장애가 전체 시스템 장애로 전파될 수 있다. 기술 Stack을 부분적으로 바꾸기 어렵다. Monolith가 나쁜 구조인 것은 아니다. 규모가 작고 강한 Transaction 일관성이 중요한 서비스라면 오히려 더 단순하고 효율적일 수 있다. 4. Microservice Architecture Microservice는 하나의 애플리케이션을 비즈니스 역량에 따라 작고 자율적인 서비스들로 구성하는 방식이다. 각 서비스는 독립된 프로세스로 실행되고 가벼운 통신 방식으로 협력한다. Client ↓ Frontend ↓ API Gateway ├─ User Service → User DB ├─ Blog Service → Blog DB └─ Order Service → Order DB 사용자 기능이 변경되었다면 User Service만 테스트하고 배포할 수 있다. User Service 수정 ↓ User Service Test ↓ User Service Build ↓ User Service만 배포 핵심 특징 작은 서비스: 하나의 명확한 비즈니스 기능에 집중한다. 독립된 서비스: 구현, 실행, 배포, 확장이 독립적이다. 응집된 서비스: 서로 관련된 기능은 하나의 서비스 경계 안에 둔다. 자율적 서비스: 담당 Team이 개발부터 배포와 운영까지 책임진다. 독립된 데이터: 서비스가 자신의 데이터에 대한 Ownership을 가진다. API Contract: 서비스는 내부 구현 대신 API 계약으로 통신한다. Bounded Context MSA의 서비스 경계는 단순히 Controller 수나 Table 수로 나누지 않는다. Domain Driven Design의 Bounded Context 를 기준으로 같은 의미와 업무 규칙을 공유하는 기능을 하나의 경계로 묶는다. CRM 전체 ├─ 고객 서비스 ├─ 계약 서비스 └─ VOC 서비스 같은 업무를 담당하는 구성원이 동일한 용어를 사용하는 것을 Ubiquitous Language 라고 한다. 서비스 경계를 정할 때 기능, 데이터, 사용자 흐름, 조직의 Ownership을 함께 살펴야 한다. 5. Monolith와 MSA 비교 기준 Monolith MSA 배포 단위 전체 Application 개별 Service 확장 전체를 함께 확장 필요한 Service만 확장 호출 주로 Process 내부 호출 Network 기반 호출 데이터 공유 DB 구성이 쉬움 Service별 DB 소유 권장 Transaction 상대적으로 단순 분산 Transaction이 어려움 장애 전체로 전파될 가능성 경계를 잘 설계하면 격리 가능 기술 Stack 통일되기 쉬움 Service별 선택 가능 운영 복잡도 비교적 낮음 매우 높음 MSA의 장점 Loose Coupling: 서비스 사이의 결합도를 낮출 수 있다. Independent Life Cycle: 서비스별 개발·테스트·배포가 가능하다. Independent Scalability: 요청이 많은 서비스만 확장할 수 있다. Isolated Failure: 장애 전파를 제한할 수 있다. Polyglot Technology: 서비스 특성에 맞는 언어와 DB를 선택할 수 있다. MSA의 단점 서비스 호출 시 Network Latency가 발생한다. 호출 대상의 장애로 전체 가용성이 낮아질 수 있다. 서비스별 DB를 사용하면 데이터 일관성과 Transaction 관리가 어렵다. 서비스 경계와 객체를 설계하는 복잡도가 높다. 배포, 로그, 인증, Monitoring 등 운영 대상이 증가한다. 분산 환경의 보상 Transaction이나 Saga Pattern은 실패한 작업을 되돌리는 흐름을 제공하지만, 하나의 DB Transaction과 같은 강한 일관성을 항상 보장하는 것은 아니다. 6. SOA와 MSA SOA와 MSA는 모두 Service 단위 설계를 사용하지만 지향점과 Service의 크기가 다르다. 구분 SOA MSA 주된 목적 Service 재사용으로 비용 절감 결합도를 낮추고 변화에 빠르게 대응 Service 크기 상대적으로 큼 작고 명확한 업무에 집중 통합 방식 ESB 중심 REST API·Message Broker 중심 공유 성향 공통 Service 공유 Service 공유를 최소화 Ownership 조직과 역할이 나뉠 수 있음 Service 담당 Team이 전체 수명주기 담당 7. Cloud Native Architecture Cloud Native는 Cloud 환경의 확장성과 자동화를 적극 활용해 Application을 개발하고 운영하는 방식이다. 네 가지 핵심 요소 Cloud Native Application ├─ Microservices ├─ Containers ├─ CI/CD └─ DevOps Architecture의 목표 확장 가능한 Architecture 수평 확장에 유연해야 한다. 부하를 여러 Instance로 분산한다. Service별로 Packaging하고 확장할 수 있어야 한다. 탄력적인 Architecture Service의 추가와 삭제를 자동으로 감지한다. 요청을 현재 실행 중인 Instance로 동적으로 전달한다. Stateless 통신을 활용해 Instance 교체를 쉽게 한다. 장애 격리 특정 Service의 장애가 다른 Service로 퍼지는 것을 제한한다. Timeout, Retry, Circuit Breaker 등을 사용해 연쇄 장애를 방지한다. 8. Antifragile System 수업 자료에서는 현대의 Cloud Native System을 Antifragile 이라는 관점으로 설명했다. Antifragile을 위한 요소 ├─ Auto Scaling ├─ Microservices ├─ Chaos Engineering └─ Continuous Deployment Resilience와 Antifragile Resilience: 장애가 발생해도 원래 상태로 회복하는 능력 Antifragile: 변화와 충격을 겪으며 시스템이 더 적응력 있게 발전하는 성질 실제 Software 설계에서는 장애를 예상하고 자동 복구할 수 있는 Resilience 가 우선적인 목표가 된다. Chaos Engineering CPU 부하, Network 지연, Instance 종료, 순간 Traffic 증가 같은 장애를 의도적으로 발생시켜 시스템이 실제로 복구되는지 검증하는 방법이다. 장애 가설 수립 ↓ 통제된 장애 주입 ↓ Monitoring ↓ 복구 여부 확인 ↓ 취약점 개선 9. Scale Up과 Scale Out 방식 의미 예시 Scale Up 한 서버의 사양을 높임 CPU 2 Core → 8 Core Scale Out 서버 Instance 수를 늘림 2대 → 10대 Scale Down 서버 Instance 수를 줄임 10대 → 2대 Cloud Native와 MSA에서는 요청량에 따라 Service Instance 수를 조절하는 Scale Out·Down 방식이 중요하다. 평상시 User Service × 2 Traffic 증가 User Service × 8 Order Service × 2 → 필요한 User Service만 확장 10. API Gateway MSA에서는 Service마다 서로 다른 IP와 Port가 존재할 수 있다. Client가 모든 Service의 주소를 직접 알고 있으면 Service가 추가되거나 주소가 변경될 때 Client도 함께 수정해야 한다. API Gateway는 Client 요청을 받는 단일 진입점이다. Client │ https://api.example.com ↓ API Gateway ├─ /users/** → User Service ├─ /orders/** → Order Service └─ /catalogs/** → Catalog Service Client는 Gateway의 외부 주소만 알면 된다. 내부 Service의 위치와 Port는 Gateway 뒤에 숨길 수 있다. 주요 역할 요청 Routing 인증·인가의 공통 처리 Load Balancing 외부망과 내부망의 분리 Logging과 요청 추적 요청·응답 변환 Gateway 자체를 배포하면 하나의 접근 주소를 가질 수 있지만, 운영 환경에서는 여러 Gateway Instance와 Load Balancer를 함께 구성해 Gateway가 단일 장애점이 되지 않도록 해야 한다. 11. Service Discovery Service Instance는 배포와 Auto Scaling에 따라 IP와 Port가 계속 변할 수 있다. 이를 코드에 직접 작성하면 매번 설정을 변경해야 한다. User Service Instance 시작 ↓ 자신의 위치 등록 Service Discovery ↓ 위치 조회 API Gateway 또는 다른 Service ↓ User Service 호출 핵심 기능 Service 등록 Service 위치 검색 Heartbeat를 통한 상태 확인 종료되거나 비정상인 Instance 제거 중요한 점은 API Gateway가 모든 Service 주소를 직접 등록하는 것이 아니라, 일반적으로 각 Service Instance가 Discovery Server에 자신을 등록한다는 것이다. Gateway는 Discovery Server를 조회해 현재 이용 가능한 Service를 찾는다. Eureka Client는 기본 설정에서 주기적으로 Heartbeat를 전송하며 대표적인 주기가 약 30초다. 이는 모든 변경 사항을 PATCH 요청으로 갱신한다는 의미가 아니라, 임대 정보 갱신과 Registry 동기화가 주기적으로 수행된다는 의미다. 12. Configuration Service Service마다 application.yml 을 따로 가지고 있으면 DB 주소, Token 설정, Logging 설정 같은 공통 설정이 중복된다. User Service/application.yml Order Service/application.yml Catalog Service/application.yml Config Server는 여러 Service의 설정을 중앙에서 관리한다. Git Configuration Repository ↓ Config Server ├─ User Service ├─ Order Service └─ Catalog Service 장점 환경별 설정을 한곳에서 관리한다. 중복 설정을 줄인다. 설정 변경 이력을 Git으로 관리할 수 있다. 개발·테스트·운영 환경 설정을 분리할 수 있다. Spring Cloud Bus를 함께 사용하면 설정 변경 Event를 Message Broker를 통해 여러 Service에 전달할 수 있다. 단, 모든 설정이 Runtime에 즉시 적용되는 것은 아니며 Refresh 지원 범위와 보안 정보 관리 방식을 함께 고려해야 한다. 13. Service 간 통신 분리된 Service도 하나의 업무를 완료하기 위해 서로 통신해야 한다. 동기 통신 - REST API Order Service │ User 정보 요청 ↓ User Service │ 응답이 올 때까지 대기 ↓ Order Service 작업 계속 응답을 즉시 받아 다음 작업을 해야 할 때 적합하다. 흐름이 직관적이다. 호출 대상이 느리거나 장애가 발생하면 호출 Service도 영향을 받는다. Timeout, Retry, Circuit Breaker가 필요하다. 비동기 통신 - Message Broker Order Service(Publisher) ↓ 주문 Event 발행 Kafka 또는 RabbitMQ ↓ Event 구독 Inventory Service(Subscriber) 발행자는 소비자의 처리를 기다리지 않는다. 순간 Traffic을 Queue에 저장해 완충할 수 있다. Service 간 결합도를 낮출 수 있다. Event 중복, 순서, 재처리, 최종적 일관성을 고려해야 한다. 수업 메모의 NEVIGATE 는 Message Queuing 제품명이 아니라 오기이며, 대표적인 Message Broker로 Kafka 와 RabbitMQ 를 사용한다. 14. Service Mesh Service Mesh는 Microservice 사이의 Network 통신 기능을 Application 코드와 분리해 Infrastructure 계층에서 처리하는 방식이다. Control Plane ├─ Configuration ├─ Routing ├─ Monitoring └─ Policy Data Plane Service A ↔ Proxy ↔ Proxy ↔ Service B 담당 기능 Service Discovery Routing과 Load Balancing 인증·인가와 암호화 Retry, Timeout, Circuit Breaking Traffic 관측과 분산 추적 Service Discovery와 API Gateway가 Service Mesh와 관련은 있지만 같은 개념은 아니다. Gateway는 주로 외부 Client의 진입점을 담당하고, Service Mesh는 주로 내부 Service 간 통신을 관리한다. 15. MSA 표준 구성요소 구성요소 역할 API Gateway / Service Router 요청을 알맞은 Service로 Routing Service Discovery 실행 중인 Service의 위치 등록·조회 Config Store 공통 설정을 중앙 관리 Runtime Platform JRE 등 Service 실행 환경 제공 Container Management Container 배포·복구·확장 Persistence Service의 데이터 저장 MOM Message 기반 비동기 통신 Telemetry Log·Metric·Trace 수집과 시각화 CI/CD Automation Build·Test·배포 자동화 Image Repository Container Image 저장 IaC 코드로 Infrastructure 구성 MOM 은 Message-Oriented Middleware를 의미하며, Publisher가 Message를 발행하고 Subscriber가 이를 소비하는 Pub/Sub 구조를 지원한다. 16. DevOps DevOps는 Development와 Operations를 분리된 단계가 아니라 하나의 지속적인 흐름으로 연결하는 문화와 실천 방식이다. 요구사항 ↓ 설계 ↓ 개발 ↓ Test ↓ 배포 ↓ Monitoring ↓ Feedback 다음 요구사항과 개선 전통적인 방식에서는 분석부터 배포까지 긴 주기로 진행되어 고객의 변경 사항을 늦게 반영할 수 있다. DevOps와 Agile은 작은 변경 단위를 빠르게 개발하고 검증하며, 운영 결과를 다시 개발에 반영한다. MSA에서는 Service를 담당하는 작은 Team이 기획, 개발, Test, 배포와 운영까지 Ownership을 가지는 구조가 중요하다. 흔히 작은 자율 조직을 Two-Pizza Team 으로 설명한다. 17. CI/CD CI - Continuous Integration 개발자의 변경 사항을 Source Repository에 자주 통합하고 자동으로 Build와 Test를 수행한다. Developer Push ↓ Git Repository ↓ Build ↓ Automated Test ↓ Artifact 생성 Continuous Delivery 실행 가능한 JAR나 Image를 배포 가능한 상태까지 자동으로 전달하지만, 운영 환경에 반영하는 최종 단계는 사람이 승인할 수 있다. Build → Test → Artifact 전달 → 수동 승인 → 운영 반영 Continuous Deployment 검증을 통과한 변경을 운영 환경까지 자동으로 배포한다. Build → Test → Artifact 전달 → 운영 자동 배포 Canary Deployment 새 버전을 일부 사용자에게 먼저 제공하고 문제가 없으면 Traffic을 단계적으로 확대한다. 1단계 Old 95% / New 5% 2단계 Old 50% / New 50% 3단계 Old 0% / New 100% Blue-Green Deployment 기존 환경과 새 환경을 동시에 준비하고 Routing만 새 환경으로 전환한다. Blue : 현재 운영 Version Green : 새 Version 배포·검증 ↓ Traffic을 Green으로 전환 ↓ 문제 발생 Traffic을 Blue로 Rollback 18. Container 가상화 Traditional Deployment Hardware └─ Operating System ├─ App A ├─ App B └─ App C Virtual Machine Hardware └─ Host OS └─ Hypervisor ├─ Guest OS + App A └─ Guest OS + App B VM마다 Guest OS가 필요하므로 격리 수준은 높지만 상대적으로 많은 Resource를 사용한다. Container Hardware └─ Host OS └─ Container Runtime ├─ Container A + App A ├─ Container B + App B └─ Container C + App C Container는 Host OS의 Kernel을 공유하면서 Process 실행 영역을 격리한다. 따라서 VM보다 가볍고 빠르게 시작할 수 있다. Dockerfile ↓ Bui
velog
곧 있을 프로젝트를 대비하기 위해 복습 겸 나중에 찾아서 보기 쉽게 퍼블리싱 개념과 단축키를 정리를 해보려고 합니당 스유에서는 화면을 뷰(View) 로 만들고, 뷰에 수식어(Modifier) 를 붙여 배치와 모양을 정함 수식어는 적용 순서에 따라 결과가 달라질 수 있음 ! I. 색상과 그라데이션 1-1) 단색 Text("Hello") .foregroundStyle(.blue) Rectangle() .fill(.orange) .foregroundStyle() : 글자나 도형의 색상 .background() : 배경 .tint() : 버튼·토글 등의 강조 색상 .opacity() : 투명도. $0$ 은 투명, $1$ 은 불투명 1-2) 그라데이션 LinearGradient( colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing ) LinearGradient : 한 방향으로 색이 이어짐 RadialGradient : 중심에서 바깥으로 퍼짐 AngularGradient : 중심을 기준으로 원형으로 색이 이어짐 그라데이션은 배경이나 도형의 채우기로 사용할 수 있음 RoundedRectangle(cornerRadius: 20) .fill( LinearGradient( colors: [.pink, .purple], startPoint: .top, endPoint: .bottom ) ) II. 도형과 모서리 Circle() Rectangle() RoundedRectangle(cornerRadius: 16) Capsule() Ellipse() Circle : 원 Rectangle : 사각형 RoundedRectangle : 모서리가 둥근 사각형 Capsule : 양 끝이 둥근 캡슐형 Ellipse : 타원 도형 색은 .fill() , 테두리는 .stroke() 로 설정함 Circle() .fill(.yellow) .frame(width: 80, height: 80) .overlay { Circle().stroke(.black, lineWidth: 2) } III. 테두리, 그림자, 자르기 Text("카드") .padding() .background(.white) .clipShape(RoundedRectangle(cornerRadius: 16)) .overlay { RoundedRectangle(cornerRadius: 16) .stroke(.gray.opacity(0.3), lineWidth: 1) } .shadow(color: .black.opacity(0.15), radius: 8, y: 4) .clipShape() : 지정한 모양대로 뷰를 자름 .overlay() : 뷰 위에 다른 뷰를 겹침 .stroke() : 도형의 테두리를 그림 .shadow() : 그림자를 추가 IV. 여백과 크기 Text("제목") .padding(.horizontal, 20) .frame(maxWidth: .infinity, alignment: .leading) .padding() : 여백 추가 .frame() : 크기와 정렬 지정 maxWidth: .infinity : 가능한 가로 공간을 모두 사용 alignment : 뷰 안의 내용 정렬 Spacer() : 남는 공간을 채워 뷰를 밀어냄 훠ᅵᄋ훠이 padding 과 background 의 순서에 따라 배경 적용 영역이 달라짐 Text("버튼") .padding() .background(.blue) // 여백까지 배경색 적용 V. 레이아웃과 정렬 VStack : 세로로 배치 HStack : 가로로 배치 ZStack : 앞뒤로 겹쳐 배치 Spacer() : 뷰 사이 간격 조절 Grid , LazyVGrid : 격자 형태로 배치 HStack { Text("왼쪽") Spacer() Text("오른쪽") } alignment 로 정렬을, spacing 으로 뷰 사이 간격을 설정함 VStack(alignment: .leading, spacing: 12) { Text("제목") Text("설명") } VI. 반복 패턴과 배경 도형을 반복 배치하면 점이나 격자 같은 패턴을 만들 수 있음 LazyVGrid( columns: Array(repeating: GridItem(.flexible()), count: 5) ) { ForEach(0..<25) { _ in Circle() .fill(.blue.opacity(0.3)) .frame(width: 8, height: 8) } } 복잡한 패턴은 반복되는 뷰를 조합하거나 Canvas 를 사용해 그릴 수 있음 VII. 글꼴과 텍스트 스타일 7-1) 기본 글꼴 스타일 Text("큰 제목") .font(.largeTitle) 자주 쓰는 기본 스타일임 !!! .largeTitle .title , .title2 , .title3 .headline , .subheadline .body , .callout .footnote , .caption , .caption2 기본 스타일은 사용자의 글자 크기 설정에 따라 조절되는 동적 타입을 지원 7-2) 글꼴 크기·굵기·디자인 직접 지정하기 Text("SwiftUI") .font(.system(size: 28, weight: .bold, design: .rounded)) size : 글자 크기 weight : 글자 굵기 design : 글꼴 디자인 주요 굵기에는 .ultraLight , .light , .regular , .medium , .semibold , .bold , .heavy , .black 이 있고 글꼴 디자인에는 .default , .serif , .rounded , .monospaced 등이 있음 7-3) 글자 꾸미기 Text("강조할 문장!!") .font(.title3) .fontWeight(.semibold) .italic() .underline() .fontWeight() : 글자 굵기 .italic() : 기울임 .underline() : 밑줄 .strikethrough() : 취소선 .tracking() : 글자 사이 간격 .lineSpacing() : 줄 사이 간격 7-4) 문단 정렬과 줄 수 Text("여러 줄로 표시되는 설명되는 뮨장") .lineLimit(2) .multilineTextAlignment(.leading) .lineLimit() : 표시할 줄 수 제한 .multilineTextAlignment() : 여러 줄 텍스트 정렬 .minimumScaleFactor() : 공간이 부족할 때 글자 축소 비율 설정 VIII. 이미지와 시스템 아이콘 Image("profile") .resizable() .scaledToFill() .frame(width: 120, height: 120) .clipShape(Circle()) .resizable() : 이미지 크기 변경 허용 .scaledToFit() : 비율을 유지하며 이미지 전체 표시 .scaledToFill() : 비율을 유지하며 영역을 채움 .clipShape() : 원하는 모양으로 자르기 시스템 아이콘은 Image(systemName:) 으로 불러옴 Image(systemName: "heart.fill") .foregroundStyle(.pink) IX. 애니메이션 애니메이션은 상태값이 바뀔 때 뷰의 변화를 부드럽게 보여주는 기능 이고 @State 로 상태를 만들고 값을 바꾸면 화면이 갱신됨 9-1) 기본 애니메이션 struct AnimationExample: View { @State private var isExpanded = false var body: some View { VStack { RoundedRectangle(cornerRadius: 16) .fill(.purple) .frame( width: isExpanded ? 220 : 100, height: isExpanded ? 160 : 100 ) Button("크기 바꾸기") { withAnimation(.easeInOut(duration: 0.3)) { isExpanded.toggle() } } } } } withAnimation 안에서 상태값을 바꾸면 그 값에 영향을 받는 뷰가 애니메이션으로 바뀜 9-2) 자주 쓰는 애니메이션 곡선 .linear : 일정한 속도로 움직임 .easeIn : 천천히 시작해 점점 빨라짐 .easeOut : 빠르게 시작해 천천히 멈춤 .easeInOut : 시작과 끝이 부드러워짐 .spring() : 말그대로 스프링처럼 피용튀용 효과를 줌 .spring(duration: 0.5, bounce: 0.25) .animation(_:value:) 방식 특정 상태값이 바뀔 때 애니메이션을 적용 Circle() .fill(isSelected ? .pink : .gray) .frame(width: isSelected ? 120 : 80) .animation(.spring(), value: isSelected) value: 엔 애니메이션 변화를 감지할 상태값을 넣음 9-3) 자주 쓰는 변형 효과 Text("움직이는 뷰") .scaleEffect(isSelected ? 1.2 : 1.0) .rotationEffect(.degrees(isSelected ? 10 : 0)) .offset(x: isSelected ? 20 : 0) .opacity(isSelected ? 1 : 0.4) .scaleEffect() : 크기 확대·축소 .rotationEffect() : 회전 .offset() : 위치 이동 .opacity() : 투명도 변경 9-4) 뷰 전환 효과 transition 은 뷰가 화면에 나타나거나 사라질 때 적용하는 효과 struct TransitionExample: View { @State private var isVisible = false var body: some View { VStack { Button("보이기 / 숨기기") { withAnimation(.easeInOut) { isVisible.toggle() } } if isVisible { Text("안녕하세요!") .padding() .transition(.opacity) } } } } .opacity : 투명하게 나타나거나 사라짐 뿅 .scale : 작아지거나 커지며 전환 .slide : 옆에서 밀려 들어오거나 나감 .move(edge: .top) : 지정한 방향에서 이동 효과를 조합할 수도 있음 .transition(.opacity.combined(with: .scale)) 9-5) 애니메이션 적용 시 주의점 애니메이션할 속성이 상태값에 연결되어 있는지 확인 transition 은 뷰가 추가되거나 제거될 때 사용 상태 변경을 withAnimation 으로 감싸면 전환을 확인하기 쉬움 모든 변화에 애니메이션을 적용하면 화면이 산만해질 수 있음 X. 버튼과 상태에 따른 스타일 Button("확인") { print("눌림") } .buttonStyle(.borderedProminent) .tint(.purple) .buttonStyle() : 버튼 모양 설정 .tint() : 버튼 강조 색상 설정 @State : 화면 안에서 변하는 상태값 저장 @State 값이 바뀌면 그 값과 연결된 화면이 다시 그려짐 XI. Xcode 단축키 기능 단축키 앱 실행 Command + R * 빌드 * Command + B 실행 중지 Command + . 코드 자동 완성 Esc 현재 파일에서 찾기 Command + F 프로젝트 전체에서 찾 기 Command + Shift + F 코드 들여쓰기 정리 Control + I 줄 복사 Command + D 줄 이동 Command + Option + [ 또는 ] 주석 처리·해제 Command + / 미리보기 새로고침 Option + Command + P 빠른 도움말 Option 을 누른 채 심볼 클릭 파일 빠르게 열기 Command + Shift + O 단축키는 Xcode 버전이나 키 설정에 따라 달라질 수 있음 Xcode > Settings > Key Bindings 에서 확인 가능 핵심 요약 !! 배치: VStack , HStack , ZStack , Grid 색과 배경: .foregroundStyle() , .background() , 그라데이션 모양과 디테일: Circle , .clipShape() , .overlay() , .shadow() 크기와 여백: .frame() , .padding() , Spacer() 글꼴: .font() , .fontWeight() , .lineLimit() 움직임: withAnimation , .animation() , .transition() 빠른 작업: Xcode 단축키 활용 SwiftUI는 뷰를 만들고 수식어를 연결해 화면을 꾸미는 방식임 수식어 적용 순서에 따라 결과가 달라질 수 있으니 순서를 바꿔가며 확인하는 게 좋타
velog
Transformer는 RNN처럼 Token을 순서대로 처리하지 않고, Attention 을 이용해 시퀀스 전체의 관계를 한 번에 계산한다. AutoEncoder는 입력 데이터를 압축한 뒤 다시 복원하면서 데이터의 핵심 특징을 학습한다. 이번에는 Transformer의 Multi-Head Attention, 위치 정보, Encoder와 Decoder 구조를 살펴본다. 이어서 MNIST 이미지에 합성곱 AutoEncoder를 적용해 원본 이미지를 복원하는 과정을 정리한다. 1. Padding Mask 배치 학습에서는 길이가 서로 다른 문장을 하나의 Tensor로 묶기 위해 짧은 문장 뒤에 <PAD> Token을 추가한다. 커피 한잔 어때 안녕 <PAD> <PAD> <PAD> 는 길이를 맞추기 위한 자리일 뿐 실제 의미가 없다. Attention이 이 위치를 참고하면 의미 없는 정보가 문맥에 섞이므로 Padding Mask 로 제외해야 한다. import torch sample_scores = torch.tensor([[2.0, 1.0, 0.5, 3.0]]) padding_mask = torch.tensor([1, 1, 1, 0]) masked_scores = sample_scores.masked_fill( padding_mask == 0, float("-inf"), ) weights_before = torch.softmax(sample_scores, dim=-1) weights_after = torch.softmax(masked_scores, dim=-1) print("Mask 전:", weights_before) print("Mask 후:", weights_after) 실행 결과: Mask 전: tensor([[0.2321, 0.0854, 0.0518, 0.6308]]) Mask 후: tensor([[0.6285, 0.2312, 0.1402, 0.0000]]) Mask가 적용된 Score는 -inf 가 된다. softmax(-inf) 의 결과는 0 이므로 해당 위치의 Value가 최종 출력에 반영되지 않는다. Score: [2.0, 1.0, 0.5, -inf] ↓ Softmax Weight: [0.6285, 0.2312, 0.1402, 0.0000] 2. Transformer Transformer는 2017년 논문 Attention Is All You Need 에서 제안된 신경망 구조다. RNN 계열 모델은 이전 시점의 계산이 끝나야 다음 시점을 처리할 수 있다. Transformer는 Self-Attention으로 모든 Token의 관계를 직접 계산하므로 병렬 연산에 유리하며, 멀리 떨어진 Token 사이의 관계도 직접 연결할 수 있다. RNN x1 → h1 → x2 → h2 → x3 → h3 Transformer x1 ─┬─ x1, x2, x3과 관계 계산 x2 ─┼─ x1, x2, x3과 관계 계산 x3 ─┴─ x1, x2, x3과 관계 계산 대표적인 Transformer 기반 모델에는 BERT, GPT, T5 등이 있다. 3. Multi-Head Attention Self-Attention을 Q, K, V 한 세트로만 계산하면 하나의 표현 공간에서 관계를 학습한다. Multi-Head Attention 은 여러 Head가 서로 다른 Q, K, V 투영을 학습해 다양한 관계를 동시에 포착하도록 만든다. 학습 결과에 따라 어떤 Head는 가까운 Token 관계에, 다른 Head는 문법적 관계나 의미적 관계에 반응할 수 있다. 각 Head의 역할을 사람이 미리 지정하는 것은 아니다. d_model = 512 num_heads = 8 head_dim = 512 / 8 = 64 d_model 은 num_heads 로 나누어떨어져야 한다. PyTorch로 확인하기 import torch import torch.nn as nn embed_dim = 8 num_heads = 2 head_dim = embed_dim // num_heads print("전체 Embedding 차원:", embed_dim) print("Head 개수:", num_heads) print("Head 하나의 차원:", head_dim) # batch=1, token=3, embed_dim=8 x = torch.randn(1, 3, embed_dim) mha = nn.MultiheadAttention( embed_dim=embed_dim, num_heads=num_heads, batch_first=True, ) attn_output, attn_weights = mha( query=x, key=x, value=x, need_weights=True, ) print("입력 Shape:", x.shape) print("출력 Shape:", attn_output.shape) print("Attention Weight Shape:", attn_weights.shape) 실행 결과: 전체 Embedding 차원: 8 Head 개수: 2 Head 하나의 차원: 4 입력 Shape: torch.Size([1, 3, 8]) 출력 Shape: torch.Size([1, 3, 8]) Attention Weight Shape: torch.Size([1, 3, 3]) 입력과 출력의 마지막 차원은 모두 8 이다. 내부에서는 두 Head가 각각 4차원 공간에서 Attention을 계산한 뒤, 결과를 합쳐 다시 8차원 표현을 만든다. Head별 Attention Weight 확인하기 nn.MultiheadAttention 은 기본적으로 여러 Head의 Weight를 평균해 반환한다. [batch, target_length, source_length] average_attn_weights=False 를 지정하면 Head별 Weight를 따로 확인할 수 있다. attn_output, head_weights = mha( query=x, key=x, value=x, need_weights=True, average_attn_weights=False, ) print(head_weights.shape) print("Head 0:\n", head_weights[0, 0]) print("Head 1:\n", head_weights[0, 1]) 실행 결과: torch.Size([1, 2, 3, 3]) 차원의 의미는 다음과 같다. [batch, num_heads, target_length, source_length] [1, 2, 3, 3] Self-Attention에서는 Query와 Key가 같은 시퀀스에서 만들어지므로 보통 target_length 와 source_length 가 같다. Attention Weight 시각화 세로축은 정보를 찾는 Query Token이다. 가로축은 참고 대상인 Key Token이다. 색이 밝을수록 해당 Key를 더 많이 참고한다. Weight는 학습 과정에서 계속 달라지므로 한 번 출력한 값만으로 Head의 역할을 단정해서는 안 된다. 4. Transformer Encoder 전체 흐름 Transformer Encoder의 한 Block은 다음 순서로 동작한다. 문장 ↓ Tokenization ↓ Token ID ↓ Token Embedding + Positional Encoding ↓ Multi-Head Self-Attention ↓ Residual Connection + LayerNorm ↓ Feed Forward Network ↓ Residual Connection + LayerNorm ↓ Encoder 출력 각 단계는 입력과 출력의 Shape을 유지하면서 Token 표현을 점차 문맥에 맞게 바꾼다. 5. Token Embedding 먼저 Token을 정수 ID로 바꾼다. vocab = { "<PAD>": 0, "커피": 1, "한잔": 2, "어때": 3, } tokens = ["커피", "한잔", "어때"] token_ids = torch.tensor([[1, 2, 3]]) print(token_ids) print(token_ids.shape) tensor([[1, 2, 3]]) torch.Size([1, 3]) nn.Embedding 은 각 Token ID를 d_model 차원의 실수 벡터로 바꾼다. vocab_size = len(vocab) d_model = 8 embedding = nn.Embedding( vocab_size, d_model, padding_idx=0, ) X = embedding(token_ids) print(X.shape) torch.Size([1, 3, 8]) Shape은 다음처럼 변한다. [batch, sequence_length] [1, 3] ↓ Embedding [batch, sequence_length, d_model] [1, 3, 8] 6. Positional Encoding Self-Attention은 모든 Token의 관계를 한 번에 계산하므로 RNN처럼 계산 순서에서 위치 정보를 얻지 못한다. 따라서 입력에 Token의 위치 정보를 별도로 넣어야 한다. 원래 Transformer 논문은 고정된 Sin과 Cos 함수를 이용한 Positional Encoding을 사용한다. 최종 입력 = Token Embedding + Positional Encoding 예를 들어 다음처럼 같은 차원의 두 벡터를 더한다. Token Embedding: [0.20, -0.40, 0.70, ...] Position 0 Encoding: [0.00, 1.00, 0.00, ...] 최종 입력: [0.20, 0.60, 0.70, ...] 모델이 각 숫자를 Token 정보와 위치 정보로 따로 읽는 것은 아니다. 같은 d_model 공간에서 두 벡터를 더해 의미와 위치가 함께 반영된 표현 을 만든다. Sin/Cos Positional Encoding 구현 import math class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=100): super().__init__() position = torch.arange( max_len, dtype=torch.float32, ).unsqueeze(1) div_term = torch.exp( torch.arange( 0, d_model, 2, dtype=torch.float32, ) * (-math.log(10000.0) / d_model) ) pe = torch.zeros(max_len, d_model) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) self.register_buffer("pe", pe.unsqueeze(0)) def forward(self, x): seq_len = x.size(1) return x + self.pe[:, :seq_len] pe 는 학습으로 바뀌는 Parameter가 아니라 공식으로 계산한 고정값이다. register_buffer() 로 등록하면 다음 특성을 얻는다. model.to(device) 를 호출할 때 모델과 함께 GPU나 MPS로 이동한다. state_dict() 에 포함되어 저장된다. Optimizer의 학습 대상 Parameter에는 포함되지 않는다. pos_encoding = PositionalEncoding(d_model) X_pos = pos_encoding(X) print("Embedding:", X.shape) print("Position 추가:", X_pos.shape) Embedding: torch.Size([1, 3, 8]) Position 추가: torch.Size([1, 3, 8]) 위치 정보를 더해도 Shape은 변하지 않는다. RoPE RoPE(Rotary Positional Embedding)는 위치 벡터를 입력 Embedding에 단순히 더하지 않는다. Attention에 사용하는 Q와 K를 Token 위치에 따라 회전시켜 상대적인 위치 관계가 점수에 반영되도록 만든다. Sin/Cos Positional Encoding Embedding에 위치 벡터를 더한다. RoPE Q와 K의 Attention 계산에 위치 관계를 반영한다. 7. Multi-Head Self-Attention 적용 위치 정보가 더해진 X_pos 를 Query, Key, Value에 모두 사용하면 Self-Attention이 된다. num_heads = 2 mha = nn.MultiheadAttention( embed_dim=d_model, num_heads=num_heads, batch_first=True, ) attn_out, attn_weights = mha( query=X_pos, key=X_pos, value=X_pos, need_weights=True, average_attn_weights=False, ) print("입력:", X_pos.shape) print("출력:", attn_out.shape) print("Head별 Weight:", attn_weights.shape) 실행 결과: 입력: torch.Size([1, 3, 8]) 출력: torch.Size([1, 3, 8]) Head별 Weight: torch.Size([1, 2, 3, 3]) Attention은 Token 사이의 정보를 섞지만 [batch, sequence_length, d_model] Shape은 유지한다. 8. Residual Connection Residual Connection은 Attention 결과만 다음 단계로 보내지 않고 원래 입력을 다시 더하는 구조다. y = x + F(x) x 는 Attention에 들어가기 전의 원래 정보다. F(x) 는 Attention이 계산한 변화 정보다. x + F(x) 는 원래 정보를 유지하면서 새로운 문맥 정보를 추가한 결과다. residual = X_pos + attn_out print(residual.shape) torch.Size([1, 3, 8]) 원소별 덧셈을 수행하므로 x 와 F(x) 의 Shape이 같아야 한다. Residual Connection은 깊은 신경망에서 정보와 Gradient가 전달될 경로를 제공해 학습을 돕는다. 9. Layer Normalization nn.LayerNorm(d_model) 은 각 Token 벡터의 Feature 차원을 기준으로 값을 정규화한다. 입력 Shape이 [batch, sequence_length, d_model] 이면 각 Token의 (d_model,) 벡터를 독립적으로 정규화한다. norm1 = nn.LayerNorm(d_model) x1 = norm1(residual) print("평균:", x1[0, 0].mean()) print("분산:", x1[0, 0].var(unbiased=False)) 실행 결과: 평균: tensor(0.) 분산: tensor(1.000) LayerNorm은 각 샘플과 Token 안에서 정규화하므로 BatchNorm보다 배치 크기의 영향을 덜 받는다. 가변 길이 시퀀스를 다루는 Transformer에 잘 맞는다. 10. Feed Forward Network Attention과 FFN은 서로 다른 역할을 한다. 구성 요소 역할 Attention 다른 Token에서 어떤 정보를 가져올지 계산한다. FFN 모은 정보를 각 Token 위치에서 독립적으로 비선형 변환한다. ffn = nn.Sequential( nn.Linear(d_model, 32), nn.ReLU(), nn.Linear(32, d_model), ) ffn_out = ffn(x1) print("입력:", x1.shape) print("출력:", ffn_out.shape) 입력: torch.Size([1, 3, 8]) 출력: torch.Size([1, 3, 8]) 동일한 FFN이 모든 Token 위치에 적용되지만, 각 Token은 다른 Token과 섞이지 않고 자신의 Feature만 변환한다. Encoder Block에는 두 개의 Sub-layer가 있으며 각각 Residual Connection과 LayerNorm이 붙는다. Self-Attention ↓ Residual + LayerNorm ↓ FFN ↓ Residual + LayerNorm norm2 = nn.LayerNorm(d_model) encoder_output = norm2(x1 + ffn_out) print(encoder_output.shape) torch.Size([1, 3, 8]) 11. Encoder에서 Padding Mask 사용하기 PyTorch의 nn.MultiheadAttention 은 key_padding_mask 에서 다음 규칙을 사용한다. False: 참고할 수 있는 실제 Token True: 참고 대상에서 제외할 PAD Token padded_ids = torch.tensor([ [1, 2, 3], [1, 3, 0], ]) padding_mask = padded_ids.eq(0) print(padded_ids) print(padding_mask) 실행 결과: tensor([[1, 2, 3], [1, 3, 0]]) tensor([[False, False, False], [False, False, True]]) Mask를 Multi-Head Attention에 전달한다. padded_x = pos_encoding(embedding(padded_ids)) masked_out, masked_weights = mha( padded_x, padded_x, padded_x, key_padding_mask=padding_mask, need_weights=True, ) print(masked_weights[1]) 두 번째 문장의 Attention Weight는 다음과 같다. tensor([[0.392, 0.608, 0.000], [0.372, 0.628, 0.000], [0.414, 0.586, 0.000]]) 마지막 열은 <PAD> 가 있는 Key 위치다. 모든 Query에서 가중치가 0 이므로 참고 대상에서 제외된 것을 확인할 수 있다. key_padding_mask 는 PAD를 Key와 Value의 참고 대상에서 제외한다. PAD 위치의 Query 출력까지 자동으로 삭제하는 것은 아니므로, 이후 Loss를 계산할 때도 PAD 위치를 제외해야 한다. 12. Causal Mask Padding Mask와 Causal Mask는 목적이 다르다. Mask 가리는 대상 사용 목적 Padding Mask 의미 없는 <PAD> 위치 길이가 다른 문장을 배치로 처리한다. Causal Mask 현재보다 뒤에 있는 미래 Token 다음 Token의 정답 누출을 막는다. 문장 나는 커피를 마신다 를 학습할 때 각 위치가 볼 수 있는 범위는 다음과 같다. Query | 나는 | 커피를 | 마신다 -------|------|--------|------- 나는 | O | X | X 커피를 | O | O | X 마신다 | O | O | O 추론할 때는 Token을 하나씩 생성하지만, 학습할 때는 정답 문장 전체를 알고 있다. Mask 없이 병렬 계산하면 앞쪽 위치가 미래의 정답 Token을 미리 볼 수 있으므로 Causal Mask가 필요하다. seq_len = 4 causal_mask = torch.triu( torch.ones(seq_len, seq_len, dtype=torch.bool), diagonal=1, ) print(causal_mask) tensor([[False, True, True, True], [False, False, True, True], [False, False, False, True], [False, False, False, False]]) True 인 오른쪽 위 영역이 미래 Token에 해당한다. 실제 Attention Weight는 다음처럼 미래 위치가 0 이 된다. tensor([[1.000, 0.000, 0.000, 0.000], [0.640, 0.360, 0.000, 0.000], [0.315, 0.311, 0.374, 0.000], [0.166, 0.159, 0.303, 0.372]]) 13. Transformer Encoder Block 구현 지금까지 확인한 구성 요소를 하나의 Encoder Block으로 묶는다. class SimpleTransformerEncoderBlock(nn.Module): def __init__( self, d_model=8
velog
Intro Kiruna 에 위치한 체크인 장소인 Högalidsskolan 로 가는게 오늘의 일정이다. 일본을 경유해 스톡홀롬에 도착한 뒤, 다시 국내선 비행기를 타고 키루나까지 이동한다. 목적지에 도착하는 데 꼬박 하루가 걸렸다. ✈︎ 첫번째 목적지 - to 스톡홀롬 스톡홀롬 직항편이 없어 어딘가를 한 번 거쳐야 한다. 보통 독일, 파리, 헬싱키 등을 경유하는데, 일정상 나의 선택지는 일본 경유밖에 없었다. 김포에서 8/5 KST 19:55에 출발해 현지시각 8/6 07:15에 스톡홀롬 알렌다 공항 에 도착했다. 김포 → 도쿄 : 2시간 20분 (환승 : 2시간 15분) 도쿄 → 스톡홀롬 : 13시간 45분 소요시간 : 총 18시간 20분 유럽을 갈 때면 보통 아시아 대륙을 가로질러 가는 경로를 먼저 생각하게 되는데, 이번에는 일본을 경유하면서 북극해 쪽을 지나가는 경로로 비행했다. 유럽을 가면서 북극해를 지나게 될 줄은 몰랐다. 단순히 비행 경로만 보고 있어도 꽤 신기한 경험이었다. 비행기에서 내려 알렌다 공항을 걸어가는데, 묘하게 긴장감이 들었다. 기분 좋은 긴장감이었다. 이런 감정은 정말 오랜만이었다. Memo. Kiruna 도착 후 알렌다 공항에서 무려 6시간을 기다려야 했기 때문에, 일단 아침부터 먹기로 했다. 공항 식당에서 간단하게 English Breakfast를 주문했다. 가격은 약 25,000원. 말로만 듣던 북유럽 물가를 아침부터 제대로 체감했다. 밥을 먹고도 시간이 한참 남아서 공항을 이곳저곳 돌아다니다, 정말 괜찮은 휴식공간을 발견했다. E gate로 가기 전, duty free 뒷문 쪽에 있는 엘리베이터를 타고 올라가면 Quiet Zone 이 있는데, 라운지처럼 좌석도 편하고 사람도 거의 없었다. 처음에는 1~2명 정도 있었는데, 나중에는 그마저도 나가서 혼자 몇 시간을 조용하게 편안히 보낼 수 있었다. ✈︎ 두 번째 목적지 - to 키루나 편하게 쉬어서 그런지 6시간이 생각보다 빨리 지나갔다. 두 번째 비행 구간은 스톡홀롬에서 키루나까지. 현지시각 13:20에 출발해 14:55에 Kiruna 공항에 도착했다. 스톡홀롬 → 키루나 : 1시간 35분 김포에서 출발한 지 꼬박 하루하고도 2시간이 더 걸려, 무려 26시간 만에 키루나에 도착했다. 하루를 그냥 비행기에 썼다. ᄒ 키루나 공항은 킬리만자로 공항처럼 상당히 작은 공항이다. 이곳을 찾는 사람들도 대부분 Kungsleden 트레킹을 목적으로 온다. 수하물 찾는 곳에 가보면 등산배낭밖에 안 보인다. 🚌 세 번째 목적지 - to Högalidsskolan 키루나 공항에서 짐을 찾고 밖으로 나오면, 피엘라벤 체크인 장소인 Högalidsskolan 으로 가는 버스를 쉽게 찾을 수 있다. 공항에서 나오는 사람들 대부분이 이 버스를 타기 때문에 절대 놓칠 일이 없다. 키루나 공항에서 체크인 장소까지는 약 30분 정도 걸린다. 본인은 체크인 장소 근처에 숙소를 예약했는데, 하루 종일 비행기를 탔으니 일단 숙소에 가서 짐을 풀고 씻은 뒤 다시 체크인 장소로 향했다. cf. 숙소인 캠프리판에서 체크인 장소까지는 걸어서 약 5분 정도 거리다. 체크인 장소에 가면 트레킹에 필요한 물품들을 받을 수 있다. 트레킹 패스, 지도, 가스, 트레시백 등이 준비되어 있고, 행동식도 여기서 수령할 수 있다. 행동식은 대략 8종류 정도 있었는데, CP3와 CP5에서도 리필할 수 있기 때문에 일단 2개만 챙겼다. 캐리어도 체크인할 때 맡기면 되고, 도착지인 Abisko 에서 다시 찾으면 된다. 그리고 가스버너에 불을 붙일 라이터가 필요했는데, 체크인 장소에서 당연히 빌리거나 살 수 있을 줄 알았다. 그런데 없었다. 다행히 10~15분 정도 걸어가면 마트가 있어서 라이터 하나를 구입했다. 마트 위치 : https://maps.app.goo.gl/iLSqLSUGQCcGfNSM6 ⛰︎ D-DAY 드디어! 내일이면 트레킹 시작이다. 아침 7시 40분 버스를 예약해뒀기 때문에 일찍 일어나 조식을 먹고 여유롭게 움직이려고 한다. Let's get it ~ 🤘
Score: 54.35Confidence: 49%
View offercnBeta.COM.TW
阿拉斯加航空正在对阿拉斯加航空、夏威夷航空的客舱进行全面升级,新增数百个高端座位,同时新建机场贵宾休息室。公司押注旅客愿意为旅行享受支付溢价,借此在未来十年提振企业利润。 阅读全文
Score: 53.42Confidence: 49%
View offercnBeta.COM.TW
媒体援引掌握财务数据的消息人士称,OpenAI年度经常性收入逼近7000亿美元;自7月以来,其企业端销售额增长超一倍。竞品Anthropic此前在企业AI落地市场占据优势,但OpenAI正在快速追赶。 阅读全文
Score: 53.42Confidence: 49%
View offerScore: 54.35Confidence: 49%
Score: 54.35Confidence: 49%
Score: 54.35Confidence: 49%