2019년 10월 12일 토요일

산책하기에 정말 좋은 계절입니다.

아침저녁으로 산책하기에 정말 좋은 날씨입니다. 휴일에는 아침에 1:30분정도 강아지 데리고 산책을 다닙니다. 고덕동, 상일동에 새로 입주한 아파트들이 많아서 내부 단지들을 돌고 있습니다. 그라시움만 4번정도 돌아보았습니다.
날씨도 좋고 걷기에도 무척 좋은 날씨 입니다. ㅎㅎ








2019년 10월 7일 월요일

iOS 13.*에 추가된 SwiftUI 연습 사용자 입력 추가하기 #3

세번째 데모는 입력 처리 추가하기 입니다. 아래의 링크에서 StartingPoing폴더에 있는 프로젝트를 오픈하면 됩니다.
사용자가 랜드마크를 보고 별표 표시를 하는 경우
스크린샷 2019-10-07 오후 4.18.52.png
StartingPoing폴더에 있는 시작 프로젝트를 오픈 합니다.
LandmarkRow파일에서 아래와 같이 수정합니다.
import SwiftUI
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
    HStack {
        landmark.image
            .resizable()
            .frame(width: 50, height: 50)
        Text(landmark.name)
        Spacer()
        
        if landmark.isFavorite {
            Image(systemName: "star.fill")
                .imageScale(.medium)
        }
    }
}
}
Section 2: List View를 필터링하기
사용자가 좋아하는 것만 필터링할 수 있습니다.
View에 상태를 추가하기 위해 @State속성을 사용할 수 있습니다.
LandmarkList를 아래와 같이 수정합니다.
import SwiftUI
struct LandmarkList: View {
@State var showFavoritesOnly = false

var body: some View {
    NavigationView {
        List(landmarkData) { landmark in
            if !self.showFavoritesOnly ||
                landmark.isFavorite {
                NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                    LandmarkRow(landmark: landmark)
                }
            }
        }
        .navigationBarTitle(Text("Landmarks"))
    }
}
}
Section 3: 상태를 토글하기 위해 컨트롤을 추가합니다.
유저가 리스트 필터링을 컨트롤하도록, 컨트롤을 추가합니다.
LandmarkList를 수정합니다.
import SwiftUI
struct LandmarkList: View {
@State var showFavoritesOnly = true

var body: some View {
    NavigationView {
        List {
            ForEach(landmarkData) { landmark in
                if !self.showFavoritesOnly ||
                    landmark.isFavorite {
                    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
        }
        .navigationBarTitle(Text("Landmarks"))
    }
}
}
다시 아래와 같이 추가합니다.
import SwiftUI
struct LandmarkList: View {
@State var showFavoritesOnly = true

var body: some View {
    NavigationView {
        List {
            Toggle(isOn: $showFavoritesOnly) {
                Text("Favorites only")
            }
            
            ForEach(landmarkData) { landmark in
                if !self.showFavoritesOnly ||
                    landmark.isFavorite {
                    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
        }
        .navigationBarTitle(Text("Landmarks"))
    }
}
}
스크린샷 2019-10-07 오후 4.11.38.png
Section 4: Storage를 위해 Observable Object를 사용한다.
좋아하는 랜드마크를 컨트롤하도록 준비하기 위해 먼저 observable object로
랜드마크 데이터를 저장해야 합니다.
observable object는 storage로 부터 SwiftUI환경으로 뷰를 바운드할 수 있습니다.
SwiftUI는 뷰에 영향을 주는 observable object에 대한 변경을 감시하고,
그리고 변경후에 뷰의 올바른 버전을 표시합니다.
Models폴더에 UserData.swift파일을 추가해서 아래와 같이 코딩 합니다.
import SwiftUI
import Combine
final class UserData : ObservableObject {
@Published var showFavoritesOnly = false
@Published var landmarks = landmarkData
}
Section 5: 뷰에 Model Object을 붙이기
UserData 오브젝트를 생성한 후에 앱의 데이터 저장으로 뷰를 적용하도록 업데이트합니다.
LandmarkList를 수정합니다.
import SwiftUI
struct LandmarkList: View {
//다음과 같이 수정한다.
@EnvironmentObject var userData: UserData

var body: some View {
    NavigationView {
        List {
            Toggle(isOn: $userData.showFavoritesOnly) {
                Text("Favorites only")
            }
            
            ForEach(userData.landmarkData) { landmark in
                if !self.userData.showFavoritesOnly ||
                    landmark.isFavorite {
                    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
        }
        .navigationBarTitle(Text("Landmarks"))
    }
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
.environmentObject(UserData())
}
}
SceneDelegate도 수정합니다.
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Use a UIHostingController as window root view controller
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(
            rootView: LandmarkList()
                .environmentObject(UserData())
        )
        self.window = window
        window.makeKeyAndVisible()
    }
}
LandmarkDetail을 수정합니다.
import SwiftUI
struct LandmarkDetail: View {
@EnvironmentObject var userData: UserData
var landmark: Landmark
var landmarkIndex: Int {
    userDAta.landmarks.firstIndex(
        where: { $0.id == landmark.id })!
}

var body: some View {
    VStack {
        MapView(coordinate: landmark.locationCoordinate)
            .frame(height: 300)

        CircleImage(image: landmark.image)
            .offset(x: 0, y: -130)
            .padding(.bottom, -130)

        VStack(alignment: .leading) {
            Text(landmark.name)
                .font(.title)

            HStack(alignment: .top) {
                Text(landmark.park)
                    .font(.subheadline)
                Spacer()
                Text(landmark.state)
                    .font(.subheadline)
            }
        }
        .padding()

        Spacer()
    }
    .navigationBarTitle(Text(verbatim: landmark.name), displayMode: .inline)
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarkData[0])
.environmentObject(UserData())
}
}
Section 6: 각 랜드마크에 대한 Favorite 버튼을 생성합니다.
LandmarkDetail을 수정합니다.
import SwiftUI
struct LandmarkDetail: View {
@EnvironmentObject var userData: UserData
var landmark: Landmark
var landmarkIndex: Int {
    userData.landmarks.firstIndex(where: { $0.id == landmark.id })!
}

var body: some View {
    VStack {
        MapView(coordinate: landmark.locationCoordinate)
            .edgesIgnoringSafeArea(.top)
            .frame(height: 300)
        
        CircleImage(image: landmark.image)
            .offset(x: 0, y: -130)
            .padding(.bottom, -130)
        
        VStack(alignment: .leading) {
            HStack {
                Text(verbatim: landmark.name)
                    .font(.title)
                
                Button(action: {
                    self.userData.landmarks[self.landmarkIndex]
                        .isFavorite.toggle()
                }) {
                    if self.userData.landmarks[self.landmarkIndex]
                        .isFavorite {
                        Image(systemName: "star.fill")
                            .foregroundColor(Color.yellow)
                    } else {
                        Image(systemName: "star")
                            .foregroundColor(Color.gray)
                    }
                }
            }
            
            HStack(alignment: .top) {
                Text(verbatim: landmark.park)
                    .font(.subheadline)
                Spacer()
                Text(verbatim: landmark.state)
                    .font(.subheadline)
            }
        }
        .padding()
        
        Spacer()
    }
}
}
struct LandmarkDetail_Preview: PreviewProvider {
static var previews: some View {
let userData = UserData()
return LandmarkDetail(landmark: userData.landmarks[0])
.environmentObject(userData)
}
}
실행하면 다음과 같습니다.

스크린샷 2019-10-07 오후 4.46.32.png
스크린샷 2019-10-07 오후 4.46.40.png

2019년 10월 6일 일요일

iOS 13.* 에 추가된 SwiftUI 두번째 데모 네비게이션과 리스트 출력입니다 #2

두번째로 리스트와 네비게이션 사용하기 입니다.
아래의 예제가 상당히 재미있습니다.
기존에 스토리보드를 많이 사용하던 개발자라면 한번 테스트해 보세요.
아래의 링크에서 샘플을 받아서 Starting폴더에 있는 프로젝트를 오픈해야 합니다.
새로운 파일들이 많이 추가되어 있습니다. 새로 제공된 시작 프로젝트를 오픈 합니다.
Section 2: Row View 생성하기
스크린샷 2019-10-07 오후 3.08.41.png
각 랜드마크의 상세를 표시하는 row를 생성합니다.
SwiftUI템플릿을 선택해서 LandmarkRow.swift를 추가합니다.
아래와 같이 입력합니다.
import SwiftUI
struct LandmarkRow: View {
var body: some View {
HStack {
landmark.image
.resizable()
.frame(width: 50, height: 50)
Text(landmark.name)
}
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
LandmarkRow(landmark: landmarkData[0])
}
}
Section 3: Row Preview를 커스터마이징하기
Xcode의 canvas는 자동으로 현재 에디터의 어떤 형식도 감지하고 표시합니다.
Preview provider는 하나 또는 그 이상의 뷰들을 리턴 합니다. 사이즈와 디바이스로 셋팅된 옵션과 함께 합니다.
import SwiftUI
struct LandmarkRow: View {
var body: some View {
HStack {
landmark.image
.resizable()
.frame(width: 50, height: 50)
Text(landmark.name)
}
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group {
LandmarkRow(landmark: landmarkData[0])
.previewLayout(.fixed(width:300, height: 70))
LandmarkRow(landmark: landmarkData[1])
.previewLayout(.fixed(width:300, height: 70))
}
.previewLayout(.fixed(width:300, height: 70))
}
}
LandmarkList라는 SwiftUI템플릿을 사용한 파일을 추가합니다. 아래와 같이 수정합니다.
import SwiftUI
struct LandmarkList: View {
var body: some View {
List {
LandmarkRow(landmarK: landmarkData[0])
LandmarkRow(landmarK: landmarkData[1])
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
Section 5: 동적인 리스트 만들기
컬렉션에서 직접 로우를 생성할 수 있습니다.
아래와 같이 LandmarkList를 수정합니다.
import SwiftUI
struct LandmarkList: View {
var body: some View {
List(landmarkData, id: .id) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
Models폴더에 있는 Landmark.swift를 살펴봅니다.
아래와 같이 id:을 삭제하고 코드를 수정합니다.
import SwiftUI
struct LandmarkList: View {
var body: some View {
List(landmarkData) { landmark in
LandmarkRow(landmark: landmark)
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
실행하면 아직은 상세화면만 출력됩니다.
스크린샷 2019-10-07 오전 11.52.40.png
Section 6: 리스트와 상세 사이에 네비게이션
import SwiftUI
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
NavigationLink(destination: LandmarkDetail()) {
LandmarkRow(landmark: landmark)
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
LandmarkList()
}
}
Section 7: Child View로 데이터 전달하기
LandmarkDetail 뷰는 상세 정보가 하드 코딩 되어 있습니다.
데이터를 넘겨 받도록 합니다.
LandMarkDetail을 아래와 같이 수정합니다.
import SwiftUI
struct LandmarkDetail: View {
var landmark: Landmark
var body: some View {
    VStack {
        MapView(coordinate: landmark.locationCoordinate)
            .frame(height: 300)

        CircleImage(image: landmark.image)
            .offset(x: 0, y: -130)
            .padding(.bottom, -130)

        VStack(alignment: .leading) {
            Text(landmark.name)
                .font(.title)

            HStack(alignment: .top) {
                Text(landmark.park)
                    .font(.subheadline)
                Spacer()
                Text(landmark.state)
                    .font(.subheadline)
            }
        }
        .padding()

        Spacer()
    }
    .navigationBarTitle(Text(verbatim: landmark.name), displayMode: .inline)
}
}
struct LandmarkDetail_Previews: PreviewProvider {
static var previews: some View {
LandmarkDetail(landmark: landmarkData[0])
}
}
LandmarkRow는 아래와 같이 수정합니다.
import SwiftUI
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
    HStack {
        landmark.image
            .resizable()
            .frame(width: 50, height: 50)
        Text(verbatim: landmark.name)
        Spacer()
    }
}
}
struct LandmarkRow_Previews: PreviewProvider {
static var previews: some View {
Group {
LandmarkRow(landmark: landmarkData[0])
LandmarkRow(landmark: landmarkData[1])
}
.previewLayout(.fixed(width: 300, height: 70))
}
}
Models폴더에 있는 Landmark는 프로토콜만 추가합니다.
import SwiftUI
import CoreLocation
struct Landmark: Hashable, Codable, Identifiable {
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coordinates: Coordinates
var state: String
var park: String
var category: Category
LandmarkList는 아래와 같이 수정합니다.
import SwiftUI
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
LandmarkRow(landmark: landmark)
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
}
struct LandmarkList_Previews: PreviewProvider {
static var previews: some View {
ForEach(["iPhone SE", "iPhone XS Max"], id: .self) { deviceName in
LandmarkList()
.previewDevice(PreviewDevice(rawValue: deviceName))
.previewDisplayName(deviceName)
}
}
}
진입점 역할을 수행하는 코드는 SceneDelegate에 있습니다.
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Use a UIHostingController as window root view controller
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: LandmarkList())
        self.window = window
        window.makeKeyAndVisible()
    }
}
실행하면 다음과 같이 네비게이션이 되고 상세 화면으로 전환할 수 있습니다.

스크린샷 2019-10-07 오후 3.00.29.png
스크린샷 2019-10-07 오후 3.00.36.png

iOS 13.* Xcode 11.1에서 작업하는 SwiftUI에 대한 데모 입니다. #1

다른 코드들은 큰 변화가 없는데 SwiftUI가 상당히 좋아 보입니다. 아직은 사용하려면 연습이 필요하고 익숙해지는 시간이 필요하지만 꽤 잘 만들어져있습니다.
선언적인 UI구성과 미리보기등이 가능합니다.
스크린샷 2019-10-07 오전 11.09.13.png
스크린샷 2019-10-07 오전 11.10.59.png
이런 앱을 직접 만들어볼 수 있는 튜토리얼 사이트가 무척 재미있게 제공됩니다.
Landmarks라는 싱글뷰 앱을 SwiftUI기반으로 생성합니다. 미리 리소스를 다운로드하면 됩니다.
기본적으로 추가된 ContentView에는 2개의 구조체가 있습니다. 미리보기는 macOS 10.15에서 지원됩니다.
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
그냥 실행해서 결과를 보면 Hello World가 출력됩니다.
아래와 같이 추가해서 텍스트뷰를 커스터마이징합니다.
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Turtle Rock")
.font(.title)
.foregroundColor(.green)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
스택뷰를 통해 쌓기를 합니다.
struct ContentView: View {
var body: some View {
VStack {
Text("Turtle Rock")
.font(.title)
Text("Joshua Tree National Park")
}
}
}
스크린샷 2019-10-07 오전 11.26.01.png
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
}
    }
        
}
}
스크린샷 2019-10-07 오전 11.31.49.png
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
}
    }
    .padding() 약간의 여유를 위해 추가한다. 
}
}
이미지 커스터마이징하기
다운로드 받은 turtlerock.jpg를 에셋에 추가합니다. 
프로젝트에 새로운 파일을 추가하면서 Swift UI템플릿을 사용합니다. CircleImage라는 이름으로 추가합니다. 
스크린샷 2019-10-07 오전 11.38.30.png
아래와 같이 코드를 수정합니다. 
import SwiftUI
struct CircleImage: View {
var body: some View {
Image("turtlerock")
.clipShape(Circle())
.overlay(
Circle().stroke(Color.gray, lineWidth: 4))
.shadow(radius: 10)
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage()
}
}
UIKit과 SwiftUI를 같이 사용할 수 있습니다.
새로운 파일을 추가하면서 Swift UI템플릿을 선택합니다. MapView라는 이름을 입력합니다.
아래와 같이 코드가 추가됩니다. 생성된 코드를 아래와 같이 수정합니다.
import MapKit
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
    let coordinate = CLLocationCoordinate2D(
        latitude: 34.011286, longitude: -116.166868)
    let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
    let region = MKCoordinateRegion(center: coordinate, span: span)
    view.setRegion(region, animated: true)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}
Section 6: 상세뷰를 조합하기
ContentView파일을 아래와 같이 수정하면 병합된 뷰를 볼 수 있습니다.
//
// ContentView.swift
// Landmarks
//
// Created by JONG DEOK KIM on 07/10/2019.
// Copyright © 2019 multicampus. All rights reserved.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
MapView()
.frame(height:300)
        CircleImage()
            .offset(y: -130)
            .padding(.bottom, -130)
        
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Spacer()
                Text("California")
                    .font(.subheadline)
            }
        }
        .padding()
            
        Spacer()
    }
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
스크린샷 2019-10-07 오전 11.52.40.png
아직은 저도 연습을 하고 있는데 재미있는 기술입니다. ^^

2019년 10월 2일 수요일

고수와의 대화, 생산성을 말하다 독서 후기입니다. 한근태 작가님의 신작입니다.

제가 요즘 너무 재미있게 읽고 있는 책들이 있습니다. 한근태님의 책들입니다. 
일생에 한번은 고수를 만나라
몸이 먼저다
그리고 고수와의 대화, 생산성을 말하다를 읽었습니다. 생각난 김에 고덕 평생학습관에 가서 "신은 디테일에 있다", "고수의 질문"도 빌려서 읽고 있습니다. 제가 정말 좋아했던 저자가 성공하는 사람들의 7가지 습관을 쓴 스티븐 코비와 경영학의 아버지라 불리는 피터 드러커를 이 저자분도 롤모델로 삼고 있네요. 한근태님의 책 5권을 모두 읽으면서 비슷한 부분들도 많지만 꼭 한번 읽어보시라고 권하고 싶은 책들입니다. 

목차
1. 단순함의 생산성
2. 집중과 몰입의 생산성
3. 소통과 휴식의 생산성
4. 조직 문화의 생산성
5. 생산성을 올리는 구체적 방법 

인상깊었던 문구들입니다. 

생산성의 핵심은 단순화와 집중이다. 복잡함을 제거한 후 가장 본질적인 곳에 에너지를 집중하는 것이다. 생산성의 반대말은 복잡함과 산만함이다. 
그런데 복잡성 제거는 어떻게 해야 할까? 바로 정체성을 명확히 하는 것이다. 우리 조직이 왜 존재하는지, 정확히 어떤 가치를 고객들에게 주는 곳인지 재정의해야 한다. 본질을 이해하고 거기에 맞는 일 외엔 모두 삭제하는 것이다. 

단순한 삶과 높은 이상(Simple Life, High Thinking). 내 삶의 모토 중 하나다. 내 삶은 단순하다. 나는 연말에도 거의 모임에 나가지 않는다. 새벽에 일어나 차를 마시며 책을 읽고 글을 쓴다. 글을 쓰다 지치면 헬스장에 가서 운동하고, 강연이나 코칭을 하고, 대중교통을 이용하거나 걸어 다니고, 쓸데없는 모임에는 가급적이면 참석하지 않는다. 
단순화는 생각처럼 쉽지 않다. 다음과 같은 노력이 필요하다. 
첫째, 본질에 충실해야 한다. 내가 누구인지, 무엇을 하는 사람인지, 내 역할은 뭔지, 그것에 충실한지를 생각해야 한다. 선생은 잘 가르치는 것이 역할이다. 본질에 충실하기 위해서는 비본질적인 것을 없애야 한다. 불필요한 것을 정리해야 한다. 
둘째, 단순함은 그 자체가 목적이 아니다. 단순함이란 정말 소중한 것을 위해 덜 소중한 것을 덜어내는 것이다. 불필요한 짐을 덜어내 정말 소중한 일에 집중하자는 것이다. 
셋째, 자신감이 필요하다. 단순한 삶은 원한다고 살 수 있는 건 아니다. 자신감이 있어야 가능하다. 자신감이 있으면 단순해질 수 있고, 단순해지면 속도가 빨라진다. 
단순하기 위해서는 솔직해야 한다. 솔직한 생각을 두려움 없이 주고받을 수 있어야 한다. 사람들이 솔직하지 못한 이유는 두렵기 때문이다. 


단순함에는 두 종류가 있다. 무식해서 단순한 것과, 고도의 계산과 시행착오를 거친 후 도달하는 단순함이 그것이다. 여기서 애기하는 건 당연히 후자의 단순함이다. 
말도 그렇다. 하수는 말이 많고 길다. 발표 내용이 떨어질수록 자료가 많아지고 시간은 길어진다. 내용이 없는 것을 분량으로 만회하려고 해서다. 실력이 떨어지는 선생일수록 설명이 길고 복잡하다. 들어도 무슨 말인지 이해가 안 간다. 왜 그럴까? 본인도 이해를 못하기 때문이다. 

지금 행복한가? 행복하기 위해서는 삶을 좀 더 심플하게 만들어야 한다. 행복은 단순함이다. 도미니크 로로는 "심플하게 산다"에서 이렇게 말했다.
첫째, 소유의 삶에서 존재의 삶으로 바꾸어야 한다. 몽골인이 소유하는 물건이 300개, 일본인은 6,000개 정도다. 사하라 사막의 유목민 투아레그족은 가볍게 짐을 꾸릴 수 있는 것만 소유한다. ==> 많이 반성하게 됩니다. 미니멀 라이프를 추구한다고 하면서 계속 구매하는 물건들이 늘어나고 있습니다. 몇주를 여행을 가고 가방 하나 정도면 사용할 물건들은 충분합니다. 조금 불편하게 적은 소유의 삶을 사는 것도 좋습니다. 
둘째, 현재 삶에 집중해야 한다. 가장 소중한 것은 무얼까? 바로 시간이다. 법정 스님은 "시간은 목숨"이라고 얘기했다. 우리가 진정 소유할 수 있는 단 한 가지는 하루하루의 시간뿐이다. 
셋째, 몸을 귀하게 여겨야 한다. 몸은 내 존재다. 몸만이 현재다. 몸이 무너지면 다 무너진다. 
넷째, 초연함을 얻어야 한다. 머릿속이 복잡한가? 걱정이 많은가? 하수는 머리가 늘 복잡하다. 고수는 홀가분하다. 
다섯째, 관계를 정리하고 혼자만의 시간을 가져야 한다. 인간관계를 통해 에너지를 얻기도 하고, 관계 때문에 힘들어하기도 한다. 단순한 삶을 위해서는 인간관계도 단순해야 한다. 만날수록 힘들고, 열 받고, 스트레스가 되는 관계는 정리해야 한다. 사람을 가려 만나되 관용을 갖는 게 좋다. 
여섯째, 명상하고 많이 읽고 많이 써야 한다. 우리 삶은 너무 외부를 향해 있다. 다른 사람, 디지털 기기 등에 하루 종일 노출되어 있다. 명상은 내면의 목소리를 듣는 행위다. 나는 누군지, 내가 원하는 것은 무언지, 내가 그런 삶을 제대로 살고 있는지 파악하는 시간이다. 쓸데없는 정보 대신 진정한 내가 하는 소리를 듣는 행위다. 
읽고 쓰는 행위는 자신을 돌보는 행위다. 글쓰기에는 마법의 힘이 있다. 글을 쓰면 원하는 것을 정확히 알 수 있다. 글을 쓰면 머릿속을 정리할 수 있다. 복잡한 생각에서 자유로워지려면 우선 그 생각을 표현해야 한다.

나는 조금 일찍 일어나 걷는 시간을 활용할 것을 권한다. 최고의 운동이기 때문이다. 사실, 우리는 대부분의 시간을 앉아서 일한다. 별도로 운동하는 시간을 내는 게 쉽지 않다. 출근길은 운동하기 좋은 기회다. 전철을 자주 이용하는 나는 에스컬레이터 대신 계단을 이용한다. 
초년에 얼마나 열심히 사느냐가 말년 운을 결정한다. 일년 농사는 봄에 어떤 씨앗을 뿌리느냐에 달려 있다. 하루의 품질은 아침을 어떻게 여느냐에 따라 좌우된다. 

대신 눈이 번쩍 뜨게 만드는 만남이 있다. 대표적인 것이 독서다. 독서만큼 좋은 만남은 없다. 읽고 싶은 책, 읽어야 할 책이 있으면 난 가슴이 설렌다. 자신과의 만남도 필요하다. 혼자 있어야 나를 만날 수 있다. 남을 만나는 것도 필요하지만 정말 중요한 나를 만나야 한다. 운동도 나를 만나는 방법이다. 근육 운동을 하나 보면 또 다른 내가 말을 걸어온다. 

교육 하나로 세상을 바꿀 수 없다. 하지만 교육 없이 세상을 바꿀 수는 없다. 교육의 목적은 사람을 변화시키고, 조직을 변화시키며, 그럼으로써 개인과 조직의 생산성을 높인다. 

생산성의 큰 축이 바로 몰입이다. 이는 시간이 갈수록 더 중요하다. 이유는 두 가지다. 첫째, 급변하는 사회에서 우리는 늘 초심자일 수밖에 없다. 가치 있는 일을 하려면 늘 새로운 것을 신속하게 학습할 수 있어야 한다. 둘째, 어중간한 결과물로는 경쟁력이 없다. 뛰어난 성과를 위해 몰입은 필수적이다. 
칼 뉴포트의 "딥 워크"를 보면, 현대인의 주의를 빼앗는 세가지가 나온다. 개방형 사무실, 소셜미디어 이메일이 그것이다. 개방형 사무실은 협업에는 좋아도 집중에는 좋지 않다. 늘 산만하다. 소셜미디어는 자신을 알리는 데는 유리할지 몰라도 고도의 집중력을 요구하는 컨텐츠 생산자들에게 부정적 영향을 끼친다. 
어렵고 가치 있는 일을 위해 육체나 정신을 한계까지 밀어붙일 때 최고의 순간이 온다. 많은 사람들은 여유가 행복을 가져온다고 생각한다. 일은 적게 하고 해변에서 많은 시간을 보내고 싶어 한다. 하지만 아이러니하게도 무료한 시간보다 일하는 시간이 즐기기 쉽다. 몰입 경험이 많을수록 삶의 만족도가 높아진다. 

"딥 워크"를 쓴 칼 뉴포트는 몰입을 일상에 접목하는 방식이 있다고 말한다. 첫째, 수도승같은 생활이다. 모든 것을 끊고 한가지 목표에 전념하는 것인데 보통 사람에게는 무리다. 둘째, 이원적 방식이다. 몰입하는 시간과 그렇지 않은 시간을 구분하는 것이다. 마지막은 기자의 방식이다. 필요할 때만 몰입하는 방식이다. 
몰입을 위해서는 자신만의 장소와 시간이 필요하기 때문이다. 나름의 작업 방식과 적절한 음식도 중요하다. 가벼운 운동도 해야 하고 미리 자료도 정리해두어야 한다. 그래야 몰입할 수 있다. 

간결함은 실력이다. 쉽게 읽히는 글이 가장 쓰기 어려운 글이다. 왜 그럴까? 간결함은 전문성에서 나온다. 간결하기 위해서는 그 분야를 완벽하게 이해해야 한다. 폭넓은 지식이 있어야 정확하게 요약할 수 있다. 

생산적인 프리젠테이션을 어떻게 할 것인가? 첫 5분간 상대와 공감대를 형성하고 상대를 무장 해제시켜야 한다. 그래서 서로간의 소통 채널을 확보해야 한다. 그게 이루어지지 않으면 쉽지 않다. 상대를 무장 해제 시키기 위해서는 무엇이 필요할까? 사실 프레젠테이션은 대화의 한 부분이다. 그런 면에서 잡담이 중요하다. 잡담을 잘해야 한다. 처음부터 본론으로 들어가는 경우도 있지만 대부분 다른 얘기를 하다 자연스럽게 본론으로 넘어간다. 
둘째, 목적을 분명히 해야 한다. 
셋째, 심플하고 클리어하게 해야 한다. 
넷째, 훈련과 반복이다. 알고 있다고 생각하는 것과 실제 아는 것은 큰 차이가 있다. 정말 아는 것은 세포가 기억하는 것이다. 몸이 기억하는 것이다. 의식하지 않아도 입에서 술술 얘기가 나올 수 있어야 한다. 지식의 지는 화살 시에 입구다. 입을 통해 말이 화살처럼 술술 나오는 것이다. 어떤 분야에 대해 아는 게 많으면 그런 일이 벌어진다. 식은 말씀 언에 찰진 흙 시다. 말을 찰흙에 새긴다는 것이다. 지식은 말과 글로 이루어졌다는 뜻이다. 연습이 완벽함을 만든다. Practice makes perfect 
다섯째, 아이컨텍트가 중요하다. 프레젠테이션은 연애와 비슷하다. 이해 당사자와 밀당을 해야 한다. 핵심은 눈맞춤이다. 눈을 보고 말해야 한다. 우린 눈빛으로 수많은 얘기를 할 수 있다. 하지만 대부분의 프레젠테이션은 파워포인트 자료를 보면서 얘기한다. 눈을 보는 대신 화면을 보고 얘기한다. 문제가 있다. 
여섯째, 기싸움에서 승리해야 한다. 프레젠테이션은 바료표장에 들어올 때부터 시작된다. 어떤 발걸음으로 들어오는지, 표정은 어떤지, 여유가 있는지, 초초해하는지... 그게 모두 발표 과정이다. 메시지 이전에 메신저인 당신을 팔아야 한다. 기싸움에서 진다면 발표는 볼 것도 없다. 

자기만의 루틴을 만들어라 
"노인과 바다"의 작가 어니스트 헤밍웨이는 매일 자신이 쓴 단어의 수를 기록할 정도로 글 쓰는 작업을 관리했다. "보봐리 부인"의 작가 귀스타브 플로베르는 "부르주아처럼 규칙적이고 정돈된 삶을 살아라. 그래야 격정적이고 독창적인 글을 쓸 수 있다"고 말했다. 
영국의 평론가 V.S. 프리체트는 "조금만 깊이 파고들면 위대한 인물들은 한결같은 공통저이 있다. 그들은 쉬지 않고 공부하고 연구했다. "

인간은 오래 집중할 수 없다. 하루에 고작 서너 시간만 집중할 수 있을 뿐이다. 어떻게 하면 집중력을 높일 수 있을지 고민한다. 그 결과 찾아낸 몇 가지 방안이 있다. 
첫째, 차를 마시는 것이다. 따뜻한 차를 마시면 속이 따뜻해지면서 기분이 좋아진다. 
둘째, 음악이다. 조용한 곳에서 글을 쓰는 것과 음악을 들으며 글을 쓰는 것은 다르다. 내겐 앙드레 가뇽의 연주곡이 잘 맞는다. 
섯째, 조명이다. 난 전체 방을 환하게 밝히는 것보다 스탠드 조명만 켜는 게 집중에 도움이 된다. 











지하철 5호선 상일역 근처에 입주를 시작한 그라시움에 다녀왔습니다.

아침에 산책 삼아서 다녀왔습니다. 9월 30일부터 입주가 시작된 것으로 들었습니다. 부동산에 매매나 전세를 구하러 온 분들이 정말 많았습니다. 고덕동, 상일동이 조금 더 멋지게 변하고 있습니다. 4932세대로 입주를 시작했으니 4인가족이라면 거의 2만명정도가 이사를 옵니다. ㅋㅋ
강남까지 출근하는 경우 50분이 걸린다는 단점이 있는 서울의 거의 끝자락에 있는 동네이지만 자연환경이나 학군이 좋은 곳입니다. 신축 아파트가 들어올때 마다 동네가 조금씩 변화되는 모습을 자주 보게 됩니다.





















요즘 많이 들리는 RAG에 대한 멋진 정리가 있어서 공유합니다. ㅎㅎ

 작년에는 ChatGPT가 크게 유행을 했는데 올해는 Gen AI, LLM, 랭체인등이 유행하고 있습니다. ㅎㅎ  RAG라는 단어도 상당히 많이 들리고 있습니다. 멋진 정리의 링크입니다.  https://brunch.co.kr/@ywkim36/146?...