반응형

[앱 이름] - 앱 정보

제작사: 메이크빌드랩스


이용약관

제1조 (목적)
본 약관은 메이크빌드랩스(이하 "회사")가 제공하는 앱 이름의 이용과 관련하여 회사와 이용자 간의 권리, 의무 및 책임사항을 규정함을 목적으로 합니다.

제2조 (정의)

  1. "서비스"란 회사가 제공하는 모바일 애플리케이션 및 관련 제반 서비스를 의미합니다.
  2. "이용자"란 본 약관에 따라 서비스를 이용하는 자를 의미합니다.

제3조 (약관의 효력 및 변경)

  1. 본 약관은 서비스를 이용하고자 하는 모든 이용자에게 적용됩니다.
  2. 회사는 필요한 경우 관련 법령을 위배하지 않는 범위에서 본 약관을 변경할 수 있으며, 변경된 약관은 서비스 내 공지를 통해 효력이 발생합니다.

제4조 (서비스의 제공 및 변경)

  1. 회사는 이용자에게 서비스를 제공합니다.
  2. 회사는 운영상, 기술상의 필요에 따라 서비스의 전부 또는 일부를 변경하거나 중단할 수 있습니다.
  3. 서비스의 변경 또는 중단 시 회사는 가능한 범위 내에서 사전에 공지합니다.

제5조 (이용자의 의무)

  1. 이용자는 관련 법령, 본 약관의 규정, 이용안내 및 서비스와 관련하여 공지한 주의사항을 준수하여야 합니다.
  2. 이용자는 서비스를 이용하여 불법 행위 또는 타인의 권리를 침해하는 행위를 하여서는 안 됩니다.
  3. 이용자는 서비스의 안정적 운영을 방해하는 행위를 하여서는 안 됩니다.

제6조 (지식재산권)

  1. 서비스에 포함된 콘텐츠, 디자인, 소프트웨어 등에 대한 지식재산권은 회사에 귀속됩니다.
  2. 이용자는 회사의 사전 동의 없이 서비스의 콘텐츠를 복제, 배포, 변경하거나 상업적으로 이용할 수 없습니다.

제7조 (면책조항)

  1. 회사는 천재지변, 전쟁, 기간통신사업자의 서비스 중지 등 불가항력적 사유로 서비스를 제공할 수 없는 경우 책임을 지지 않습니다.
  2. 회사는 이용자의 귀책사유로 인한 서비스 이용 장애에 대하여 책임을 지지 않습니다.
  3. 회사는 무료로 제공하는 서비스와 관련하여 관련 법령에 특별한 규정이 없는 한 책임을 지지 않습니다.

제8조 (분쟁 해결)

  1. 본 약관에 관한 분쟁은 대한민국 법률을 준거법으로 합니다.
  2. 서비스 이용과 관련하여 발생한 분쟁에 대해서는 민사소송법상의 관할법원에 소를 제기할 수 있습니다.

부칙
본 약관은 [시행일]부터 시행합니다.


고객 지원

서비스 이용 중 문의사항이나 불편사항이 있으시면 아래 연락처로 문의해 주세요.

  • 이메일: theallenkim@gmail.com
  • 제작사: 메이크빌드랩스
  • 응답 시간: 영업일 기준 1~3일 이내

© 메이크빌드랩스. All rights reserved.

반응형
Posted by onlyTheOne
,
반응형
메이크빌드랩스 - 개인정보 처리방침 | Privacy Policy
MakeBuildLabs

개인정보 처리방침

Privacy Policy

시행일: 2025년 2월 23일

Effective Date: February 23, 2025

1. 개요

메이크빌드랩스(이하 "회사")는 이용자의 개인정보를 소중히 여기며, 「개인정보 보호법」 등 관련 법령을 준수합니다. 본 개인정보 처리방침은 회사가 제공하는 모든 모바일 애플리케이션(이하 "서비스")에 적용됩니다.

2. 수집하는 개인정보

회사는 서비스 제공을 위해 이용자의 개인정보를 별도로 수집하지 않습니다.

회사의 앱은 기기 내에서 독립적으로 작동하며, 별도의 회원가입·로그인 절차가 없고, 이용자의 개인정보를 서버로 전송하거나 저장하지 않습니다.

3. 자동 수집 정보

회사는 광고 SDK, 분석 도구 등 제3자 서비스를 사용하지 않으며, 이용자의 기기 정보, 사용 통계 등을 자동으로 수집하지 않습니다.

다만, 앱이 배포되는 플랫폼(Apple App Store, Google Play Store)에서 자체적으로 수집하는 정보(앱 설치 통계, 충돌 보고서 등)가 있을 수 있으며, 이는 각 플랫폼의 개인정보 처리방침에 따릅니다.

4. 기기 접근 권한

일부 앱은 서비스 제공을 위해 카메라 등 기기 접근 권한을 요청할 수 있습니다. 해당 권한은 앱 내 기능 구현 목적으로만 사용되며, 촬영된 이미지나 데이터는 기기 내에서만 처리되고 외부로 전송되지 않습니다.

5. 개인정보의 제3자 제공

회사는 이용자의 개인정보를 제3자에게 제공하지 않습니다.

6. 개인정보의 보유 및 파기

회사는 개인정보를 수집하지 않으므로 별도의 보유·파기 절차가 없습니다. 앱 내에서 생성된 데이터는 이용자의 기기에만 저장되며, 앱 삭제 시 함께 제거됩니다.

7. 이용자의 권리

이용자는 언제든지 앱을 삭제하여 기기에 저장된 모든 데이터를 제거할 수 있습니다. 개인정보와 관련한 문의사항이 있으시면 아래 연락처로 문의해 주시기 바랍니다.

8. 개인정보 보호책임자

이 부분을 실제 연락처로 수정해 주세요.
예시: 이름: 홍길동 / 이메일: contact@makebuildlabs.com

9. 개인정보 처리방침 변경

본 개인정보 처리방침이 변경되는 경우, 변경 사항을 본 페이지에 게시하며, 변경된 방침은 게시한 날부터 효력이 발생합니다.

1. Overview

MakeBuildLabs ("Company") values the privacy of its users and complies with applicable privacy laws and regulations. This Privacy Policy applies to all mobile applications ("Services") provided by the Company.

2. Personal Information We Collect

We do not collect any personal information from our users.

Our apps operate independently on your device. There is no account registration or login required, and no personal data is transmitted to or stored on any external server.

3. Automatically Collected Information

We do not use any third-party services such as advertising SDKs or analytics tools, and we do not automatically collect device information or usage statistics.

However, the platforms through which our apps are distributed (Apple App Store, Google Play Store) may independently collect certain data (e.g., installation statistics, crash reports) in accordance with their own privacy policies.

4. Device Permissions

Some apps may request device permissions such as camera access to provide their intended functionality. These permissions are used solely within the app, and any captured images or data are processed locally on your device and are never transmitted externally.

5. Sharing with Third Parties

We do not share any personal information with third parties.

6. Data Retention and Deletion

Since we do not collect personal information, there are no data retention or deletion procedures. Any data generated within the app is stored only on your device and is removed when the app is uninstalled.

7. Your Rights

You may delete the app at any time to remove all locally stored data. If you have any questions regarding privacy, please contact us using the information below.

8. Contact Information

Please replace this section with your actual contact details.
Example: Name: John Doe / Email: contact@makebuildlabs.com

9. Changes to This Policy

If this Privacy Policy is updated, the changes will be posted on this page and will take effect from the date of posting.

반응형
Posted by onlyTheOne
,
반응형

겁나 오래 살아 남아 있는 iOS 개발자가 RxSwift와 Combine 처음 봤을 때

iOS 개발을 2009년 부터 시작한 고인물 개발자 입니다. 너어어어무 고전 기술? 로만 앱을 만들고 유지보수 해봐서 그런지 RxSwift와 Combine을 처음 봤을 때 솔직히 당황했습니다.

"이게 뭐지? 왜 이렇게 복잡하게 하지?"

혹시 저처럼 느끼신 분들을 위해 정리해봤습니다.


먼저, 둘의 관계

RxSwift

  • 서드파티 라이브러리
  • iOS 8부터 사용 가능
  • 2015년부터 사용됨

Combine

  • Apple 공식 프레임워크
  • iOS 13 이상만 지원
  • 2019년 WWDC에서 발표

결론부터 말하면, 개념은 거의 똑같고 이름만 다릅니다.

RxSwift를 알면 Combine은 쉽고, Combine을 알면 RxSwift도 쉽습니다.


용어 비교표

개념 RxSwift Combine
데이터 발행 Observable Publisher
데이터 구독 Observer Subscriber
구독 해제 dispose() cancel()
메모리 관리 DisposeBag Set<AnyCancellable>
구독 메서드 subscribe sink
값 래퍼 BehaviorRelay @Published

기존 UIKit 방식 (우리가 익숙한 것)

버튼을 누르면 라벨 텍스트가 바뀌는 코드입니다.

button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)

@objc func buttonTapped() {
    label.text = "눌렸다"
}

직관적이죠? 버튼 눌리면 → 함수 실행 → 라벨 변경

10년간 이렇게 해왔습니다.


RxSwift 방식

let disposeBag = DisposeBag()

button.rx.tap
    .subscribe(onNext: { _ in
        label.text = "눌렸다"
    })
    .disposed(by: disposeBag)

Combine 방식

var cancellables = Set<AnyCancellable>()

button.publisher(for: .touchUpInside)
    .sink { _ in
        label.text = "눌렸다"
    }
    .store(in: &cancellables)

나란히 비교하면 거의 똑같음

보시면 구조가 동일합니다.

단계 RxSwift Combine
1. 이벤트 소스 button.rx.tap button.publisher(for:)
2. 구독 .subscribe(onNext:) .sink { }
3. 메모리 관리 .disposed(by: disposeBag) .store(in: &cancellables)

핵심 개념 3가지 (둘 다 동일)

1. Publisher / Observable (발행자)

데이터를 내보내는 쪽입니다.

"나 값 바뀌면 알려줄게"라고 선언하는 겁니다.

Combine:

@Published var name: String = ""

RxSwift:

let name = BehaviorRelay<String>(value: "")

2. Subscriber / Observer (구독자)

데이터를 받는 쪽입니다.

Combine:

$name
    .sink { newName in
        print("이름 바뀜: \(newName)")
    }

RxSwift:

name
    .subscribe(onNext: { newName in
        print("이름 바뀜: \(newName)")
    })

3. Operator (연산자)

중간에서 데이터를 가공합니다. 검색창 예시로 보겠습니다.

Combine:

$searchText
    .debounce(for: 0.3, scheduler: RunLoop.main)
    .filter { $0.count >= 2 }
    .sink { text in
        self.search(text)
    }
    .store(in: &cancellables)

RxSwift:

searchText
    .debounce(.milliseconds(300), scheduler: MainScheduler.instance)
    .filter { $0.count >= 2 }
    .subscribe(onNext: { text in
        self.search(text)
    })
    .disposed(by: disposeBag)

거의 똑같죠? debounce, filter 같은 연산자 이름도 동일합니다.


메모리 관리 비교 (중요!)

이거 빼먹으면 둘 다 동작 안 합니다.

Combine:

var cancellables = Set<AnyCancellable>()

$name
    .sink { print($0) }
    .store(in: &cancellables)  // 필수!

RxSwift:

let disposeBag = DisposeBag()

name
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)  // 필수!

역할은 같습니다. 뷰컨트롤러가 해제될 때 구독도 같이 정리하는 거예요.


그래서 뭘 써야 하나요?

RxSwift를 써야 할 때:

  • iOS 13 미만 지원해야 할 때
  • 이미 RxSwift로 된 프로젝트 유지보수할 때
  • RxCocoa의 UI 바인딩이 필요할 때

Combine을 써야 할 때:

  • 새 프로젝트 시작할 때
  • iOS 13 이상만 지원해도 될 때
  • SwiftUI 쓸 때 (Combine 기반이라 궁합 좋음)
  • 외부 의존성 줄이고 싶을 때

솔직한 후기

처음엔 "왜 이렇게 복잡하게?" 싶었습니다.

근데 검색창처럼 "입력 → 딜레이 → 필터 → API 호출" 같은 흐름은 확실히 코드가 줄어듭니다.

UIKit으로 같은 걸 만들려면 Timer 쓰고, 플래그 변수 만들고, 취소 로직 넣고... 더 복잡해요.

아직 완벽히 익숙하진 않지만, 하나씩 적용해보는 중입니다.

하나만 익히면 다른 하나는 자동으로 따라옵니다. 용어만 다르니까요.


위 내용은 클로드에게 물어가며 적어 본 내용입니다.

반응형
Posted by onlyTheOne
,
반응형

 Claude AI 를 이용해 Tuist를 공부하려고 봤더니... Tuist 설치 부터가 최신 자료가 아니였다.... (쿨럭)

그래서 현재 SKT 회선을 보유한 나로써는 병행해서 사용 하고 있는 Perplexity 서비스를 이용해 보았다.

일단...... 먼저 말하면.... 잘못된 정보? 라고 하기 애매한 ...

암튼 설치 하다 삽질을 더 했다. Tuist 라이브러리 설치에만 무려 오랜 시간이 소요 되었다. 

심지어... 예전 Tuist 설치 방식을 계속 알려주고 있었다... (이건 서비스 로직에 사소한 버그가 있지 않나.... 의심스럽다.) 

Brew 방식으로 설치하라고 해서 설치했다가..... ---> 삭제

RTX 방식으로 설치하라고 해서..... 설치 했다가 ----> 삭제 

그래서 물어봤다.

와........ 진심 화가나기 시작 하였다.... 

 

mise 라는걸 이용해서 설치하란다..... 근데...... 이상하게....... 2번 부터 안되었다... ( 하.... 머지...)

머지?........ 하아... 이때 부터 갑자기 쌔함이......... 

다음과 같은 과정을 진행했다.

 

먼가 잘되는거 같아 보였는데....

그리고 다음과 같은 일련의 과정을 해보니........

 

와........ 역시나..... 쌔한 기운은.... 어디 많이 못간다..... 

Tuist 명령어가 안되기 시작..... 

또 다시... 물어보고....

(과정은 생략했다...)

또 안되서 또 다음과 같이 물어보고...

 

하라는 해도 상황이 똑같다.... ㅋㅋㅋ

또 물어보았다...

(과정 또 생략....)

역시나 문제가 또 발생 다시 또 물어봄...

(과정은 역시나 생략.... , 왜나고? 똑같아서...)

머 당연히 문제 해결은 안되었기에...

과정은..... 또........  생략... 

 

그리고........ 

 

이쯤 되면........ 이제 누가 이기는지 해보자고 전투 모드로 전환....

역시나 또 안.... 되는...

열심히 알려주는데.... 먼가 내용이 똑같은? 게 많다.... 

하라는대로 해보니... 역시나 문제가 발생.... 또 물어봤다!

아니나 다를까.... 안된다. ... .(쿨럭 )

삽질을 통해서... 결국.... 되었다.

 

그래서..... tuist로 프로젝트가 생성되는지도 알아보았다. 

와.... 여기까지 하니... 새로운 문제가... 

(예전에는 구글을 뒤졌다면 지금은.... perplexity에게 물어본다. )

그래서 또 물어보았다...  

그러고 나서.... 궁금한게 생각나서 추가 질문을 하였다.

이렇게 해서......Tuist를 설치 했다.....

그리고 프로젝트 파일도 만들었더니.........

나는 만든 프로젝트가 UIKit 기반일줄 알았는데...... 

swiftui가 기본이였다....  ( 쿨럭 )

그래서 또 물어보았다...

 

 

그랬던 것이였다... 

다음 시간에는 UIkit 기반으로 만들어 봐야 겠다.

반응형
Posted by onlyTheOne
,
반응형

최근 공부를 시작하면서 최신?이라고 하긴 좀 어렵지만 실무에서 사용하지 못해왔던 여러가지들을 찾아 가며 공부를 해보고 있다 

물론 써봐야 확실히 감이 오지만 

 

오늘은 ...... 디자인 패턴? 이라고 해야 할거 같은데 MVVM과 TCA 차이점에 대해 알아 보고자 한다. 

제목과 같이 질문을 하니........ 다음과 같이 알려주었다. 

그리고 둘의 차이점에 대한 코드를 요청 하였다 

한번 더 살펴 보아야 겠다.....

좀 더 물어 봐야 겠음.

반응형
Posted by onlyTheOne
,
반응형

claude로 어떻게 공부를 할 수 있을까 궁금해서 기존에 필터 앱을 업데이트 하고자 다음과 같이 요청 하였습니다. 

"새로운 질문을 하지 swift 언어로 iOS 15 이상에서 카메라앱을 만들거야 동영상 촬영 앱이고 기능은 다음과 같아 1. 동영상 촬영 버튼을 누르면 촬영이 되고 10초뒤에 촬영이 자동 종료 되 2. 동영상 필터는 2개가 제공될거야 3. 필터는 흑백하고 카툰 필터 4. 녹화된 영상은 앱내에 저장하는데 사진앱으로 내보내기 기능을 제공 5. 녹화사에는 전면 카메라 녹화나 후면 카메라 녹화 둘중 하나만 되게 할거야 6. 고급 카메라는 PIP 기능으로 영상이 녹화되게 할거야 7. 카메라 촬영중에는 화면에서 10초 타이머가 보여지면서 촬영된 영상이 실시간으로 보이게 할거야 여기까지 "

이렇게 요청 하니 CIFilter 기반 이 나왔다.....

CIFilter로는 퍼포먼스가 안나오는걸 알고 있기에... 다시 요청 했다.

"아 미안해... metal framework로 구현해줘 CIFilter는 퍼포먼스 안나와"

다음과 같이 안내해 주었다.

코드를 보니.... 분류가 안되어 있어 재차 요청 했다.

"파일명 별로 구분좀 해줄 수 있을까?"

 

위와 같이 프로젝트 구조도 알려주었다. 

 

그래서 각각의 소스코드는 다음과 같다. 

// MARK: - Models/ShaderTypes.swift
struct Vertex {
    var position: SIMD3<Float>
    var textureCoordinate: SIMD2<Float>
}

enum FilterType {
    case normal
    case grayscale
    case cartoon
}

// MARK: - Shaders/CameraShaders.metal
"""
#include <metal_stdlib>
using namespace metal;

struct VertexIn {
    float3 position [[attribute(0)]];
    float2 textureCoordinate [[attribute(1)]];
};

struct VertexOut {
    float4 position [[position]];
    float2 textureCoordinate;
};

vertex VertexOut vertexShader(uint vertexID [[vertex_id]],
                             constant VertexIn *vertices [[buffer(0)]]) {
    VertexOut out;
    out.position = float4(vertices[vertexID].position, 1.0);
    out.textureCoordinate = vertices[vertexID].textureCoordinate;
    return out;
}

fragment float4 fragmentShader(VertexOut in [[stage_in]],
                             texture2d<float> texture [[texture(0)]],
                             constant int &filterType [[buffer(1)]]) {
    constexpr sampler textureSampler(mag_filter::linear,
                                   min_filter::linear);
    
    float4 color = texture.sample(textureSampler, in.textureCoordinate);
    
    // Normal
    if (filterType == 0) {
        return color;
    }
    // Grayscale
    else if (filterType == 1) {
        float gray = dot(color.rgb, float3(0.299, 0.587, 0.114));
        return float4(gray, gray, gray, color.a);
    }
    // Cartoon
    else if (filterType == 2) {
        float3 original = color.rgb;
        
        float2 texelSize = float2(1.0 / texture.get_width(),
                                1.0 / texture.get_height());
        
        float3 edge = float3(0.0);
        for(int i = -1; i <= 1; i++) {
            for(int j = -1; j <= 1; j++) {
                float2 offset = float2(float(i), float(j)) * texelSize;
                float3 sample = texture.sample(textureSampler,
                                            in.textureCoordinate + offset).rgb;
                edge += abs(sample - original);
            }
        }
        
        float3 quantized = floor(original * 5.0) / 5.0;
        float edgeMask = 1.0 - smoothstep(0.2, 0.3, length(edge));
        return float4(quantized * edgeMask, color.a);
    }
    
    return color;
}
"""

// MARK: - Controllers/MetalCameraController.swift
class MetalCameraController: NSObject {
    private var captureSession: AVCaptureSession?
    private var videoOutput: AVCaptureMovieFileOutput?
    private var textureCache: CVMetalTextureCache?
    
    private let metalDevice: MTLDevice
    private let metalCommandQueue: MTLCommandQueue
    private let pipelineState: MTLRenderPipelineState
    private let vertexBuffer: MTLBuffer
    
    private var currentFilterType: FilterType = .normal
    
    private let vertices: [Vertex] = [
        Vertex(position: SIMD3<Float>(-1, -1, 0), textureCoordinate: SIMD2<Float>(0, 1)),
        Vertex(position: SIMD3<Float>(1, -1, 0), textureCoordinate: SIMD2<Float>(1, 1)),
        Vertex(position: SIMD3<Float>(-1, 1, 0), textureCoordinate: SIMD2<Float>(0, 0)),
        Vertex(position: SIMD3<Float>(1, 1, 0), textureCoordinate: SIMD2<Float>(1, 0))
    ]
    
    override init() {
        // ... (이전 코드와 동일)
    }
    
    private func setupTextureCache() {
        // ... (이전 코드와 동일)
    }
}

// MARK: - Views/Components/CameraView.swift
class CameraView: MTKView {
    var renderDelegate: CameraRenderDelegate?
    
    override init(frame frameRect: CGRect, device: MTLDevice?) {
        super.init(frame: frameRect, device: device)
        setupView()
    }
    
    required init(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }
    
    private func setupView() {
        self.device = MTLCreateSystemDefaultDevice()
        self.backgroundColor = .black
        self.framebufferOnly = false
    }
}

// MARK: - Views/Components/CameraControls.swift
class CameraControls: UIView {
    var onRecordTap: (() -> Void)?
    var onFilterTap: (() -> Void)?
    
    private lazy var recordButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("녹화", for: .normal)
        button.backgroundColor = .red
        button.layer.cornerRadius = 25
        button.addTarget(self, action: #selector(recordButtonTapped), for: .touchUpInside)
        return button
    }()
    
    private lazy var filterButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("필터 변경", for: .normal)
        button.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
        return button
    }()
    
    private lazy var timerLabel: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.font = .systemFont(ofSize: 24, weight: .bold)
        label.text = "10"
        label.textAlignment = .center
        return label
    }()
    
    @objc private func recordButtonTapped() {
        onRecordTap?()
    }
    
    @objc private func filterButtonTapped() {
        onFilterTap?()
    }
    
    // ... Layout 코드
}

// MARK: - ViewControllers/MetalCameraViewController.swift
class MetalCameraViewController: UIViewController {
    private var cameraView: CameraView!
    private var cameraControls: CameraControls!
    private var cameraController: MetalCameraController!
    
    private var currentFilter: FilterType = .normal
    private var recordingTimer: Timer?
    private var remainingTime: Int = 10
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupCameraController()
        setupBindings()
    }
    
    private func setupViews() {
        cameraView = CameraView(frame: view.bounds, device: MTLCreateSystemDefaultDevice())
        cameraControls = CameraControls()
        
        view.addSubview(cameraView)
        view.addSubview(cameraControls)
        
        // Layout constraints
    }
    
    private func setupCameraController() {
        cameraController = MetalCameraController()
    }
    
    private func setupBindings() {
        cameraControls.onRecordTap = { [weak self] in
            self?.handleRecordTap()
        }
        
        cameraControls.onFilterTap = { [weak self] in
            self?.handleFilterTap()
        }
    }
    
    private func handleRecordTap() {
        if recordingTimer == nil {
            startRecording()
        } else {
            stopRecording()
        }
    }
    
    private func handleFilterTap() {
        switch currentFilter {
        case .normal: currentFilter = .grayscale
        case .grayscale: currentFilter = .cartoon
        case .cartoon: currentFilter = .normal
        }
        // Update filter in camera controller
    }
    
    // ... Recording and timer related methods
}

// MARK: - Utils/VideoExporter.swift
class VideoExporter {
    static func exportVideo(at url: URL, completion: @escaping (Result<URL, Error>) -> Void) {
        // Video export implementation
    }
}

// MARK: - Protocols/CameraRenderDelegate.swift
protocol CameraRenderDelegate: AnyObject {
    func renderFrame()
    func updateFilter(_ type: FilterType)
}

여기까지...  

직접 코드를 나눠어 넣었을때.... MetalCameraViewController.swift 파일에 먼가 이상함을 감지했다. 

to be continue...

반응형
Posted by onlyTheOne
,
반응형

최근 Claude 를 활용해서 필터 효과가 동영상 제작 앱을 하나 만들어 보았습니다. 

마무리 직압 중인데.... 무언가 남겨야 할 거 같아...

시작글을 올릴려고 합니다. 

이 동영상 촬영 앱은

Metal Framework를 사용한 소스코드이며 녹화와 화면에 실시간으로 보여지도록 되어 있습니다.

Swift 언어 기반에 SwiftUI는 사용하지 않았습니다. 

내일 부터 정리해서 올리도록 하겠습니다. 

반응형
Posted by onlyTheOne
,
반응형

지난 편에 이어서...  (링크 : https://opendev4u.tistory.com/181 )

최초에 질문 했을때 combine 사용한 예시 코드를 설명해 주었다. 

그래서 다음과 같이 다시 물어보았다.

"그럼 mvvm 구현을 위해 combine을 써야 한다고 말해줘야 하는거 아닌가?" 

라고 물었더니 다음과 같이 피드백을 주었다. 

다음과 같이 피드백을 주었다. 

MVVM 을 구현 한 소스코드 예시는 크게 3가지 방법을 알려주었다. 

Combine 없이 구현 하는 방법 / Combine 이용하는 방법 / RxSwift 이용하는 방법 

그리고 부가 설명을 해주었다. 

과연 이것만 가지고 이해가 될까?? 아직 부족해 보이고... 아하 하기엔 애매한 면이 있어

더 공부 해 볼겸 해서 좀 더 물어보면서 파보기로 했다.

#3 에 이어서...

반응형
Posted by onlyTheOne
,
반응형

나는 아직 MVVM을 제대로 사용해 본적이 없다.

MVVM 을 사용할 만한 서비스 앱을 아직 운영해보거나 개발해 본적이 없다.

개인 스터디로 하기엔 먼가 한계가 있어 보였다. 

그래서 최근 쓰기 시작한 Claude 를 통해 MVVM을 정리해보고자 한다. 

다음과 같이 질문 했다.

"mvvm 에 대해서 설명해줘 나는 도무지 모르겠어 이해가 안되... 이해좀 시켜주었으면 해" 

Claude의 답은 다음과 같았다 .

< 스크린샷 1번 : Claude가 설명해 준 mvvm 에 대한 답변 >

 

MVVM의 핵심은... 내가 잘못 이해 했을 수도 있지만 

- 데이터 바인딩을 통한 자동 업데이트 

이지 않을까 싶다. 

그 이유는 Claude가 설명해 주며 Combine을 활용한 코드를 안내해 주었다.

이 소스코드를 보면서 일부 생략? 누락? 한 듯한 로직이 보여서 눈으로 읽어 컴파일 돌려 보았다. 

눈에 뜨던게 .sink 라는 블록 함수? 와 &cancellables 라는 항목명이 눈에 띄었다.

이건 머지?라고 인터넷에서 검색을 하고 나서 보니.... Combine을 이용한 코드 였던 것이다. 

나는 Combine에 대한 존재는 알고 있었다. mvvm을 좀더 쉽게?구현하는데 RxSwift라는 3rd party 라이브러리가 있고 

이것과 유사한 라이브러리가 Apple에서 제공해준게 Combine 이였기 때문이다. 

그런데 코드가 누락된? 부분이 많아 실제 컴파일 돌리기엔 리스크가 컸다. (사실 컴파일 에러도...)

먼가 이상함을 느껴 다시 Claude에게 질문해 보았다.

"이 코드에 .compactMap하고 sink 함수는 rxswift에 있는거야? 아님 combine에 있는거야" 

답은 다음과 같이 나왔다. 

 

내용을 보면 RxSwift, Combine비교 내용도 있고 어떤 상황이냐에 따라 어떤걸 쓸지도 알려주고 있었다. 

To be continue...

반응형
Posted by onlyTheOne
,
반응형

claude 유료로 사용하면서 SKT 고객이라 perplexity를 사용하고 있다. 

한달여 사용하면서 느낀것은.... perplexity와 claude와 성격이 다르다는 점이다. 

느낀점을 적어보면

perplexity는 검색 결과 기반의 데이터를 가지고 AI로 가공한 것으로 보여지고 

그러다 보니 검색 키워드에 따라 검색 결과가 달라지니 AI 모델 결과도 달라진다. 

10년 넘게 프로그래밍 관련 일을 한 프로그래머로써 내 지식 중 하나를 검색했을때 과연 내가 아는 결과가 나오는지 궁금했다. 

대표적으로 GPUImage와 VideoToolbox에 대해서 직접적으로 물어보지 않고 간접적으로 물어봤다. 

그 결과 내가 원하는 답을 얻기 위해서 무려 10번의 재 질문이 필요했다. 

검색 주제는 다음과 같다

"스위프트로 동영상 생성하는 기능을 만들고 싶다. 관련 라이브러리를 알려줘"

이게 기본 질의 였고 그 다음으로 이것을 좀 더 디테일하게 물어봤다 

예를 든다면 "애플에서 제공해주는 공식은 없어?", "라이센스 이슈는 피했으면 좋겠어", "ffmpeg 빼고 이야기 해줄래?" 

GPUImage는 마치 얻어 걸린것 처럼 표시 해주었으며

videotoolbox는 내가 직접 말하기 전까지는 아에 찾아 내지도 못했다. 

내가 느끼기엔 perplexity를 기술적 용도로 활용하기엔 한계가 좀 보였다. 

그리고 claude에 대해서 ...

공부할겸 최신 기법들을 에제와 함께 요청했더니 코드는 잘 구현해 준다. 

그런데..... 완성도가 높지 않다. 

이 말은 경험이 많은 개발자가 직접 달라 붙어서 좀더 완성도 있는 코드를 요청해야 하며 

이 코드를 그대로 쓰면 안되고 분석을 통해 로직 확인을 거친다음 써야 한다는 것이다.

심저어 metal 관련 쉐이더 코드도 작성해 준다. 

카메라 필터 앱을 만들어 볼려고 코드를 요청 했더니 다 작성해주었다 

그런데...... 동작은 제대로 안하는 경우가 종종 있고.... 주의사항에 도 나와 있다....

 

즉... 나온 코드를 로직 검증 작업을 하고 이용 해야 하는 것이다. 

경험자와 처음 접해본 사람의 차이는.... 코드에 대한 빠른 검사 이다. 

그리고 이 걸로 앱 코딩 된다고 하는 분들은...... 경험자 이거나..... 

개발에 아무것도 모르는 분들이 이것을 가지고 프로그래밍 할 수 있다...

개발에 타고 난 사람이 적성을 늦게 찾은 것 뿐이다. 

이걸 가지고 코딩한다고 해서 개발자라고 다년간 경험을 가진 개발자들을

무시하는 행위를 하는 선무당이 없었으면 한다.....

 

그리고......... 너무 의존하지 않았음 한다..... 어시스트 정도로 생각해야 하지 메인으로 접근하면 안된다.

 지금은 저렴하게 이용 가능한것처럼 보이겠지만 

없으면 일을 할수 없는 수준이 되면.... 개발자 몸값보다 더 비싸게 이용해야 할 것이다.

 

반응형
Posted by onlyTheOne
,