์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Titanium์œผ๋กœ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœํ•˜๊ธฐ: ํ•œ ๋ฒˆ์˜ ์ฝ”๋”ฉ์œผ๋กœ iOS์™€ Android๋ฅผ ๋™์‹œ์—!

๐Ÿš€ Titanium์œผ๋กœ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœํ•˜๊ธฐ: ํ•œ ๋ฒˆ์˜ ์ฝ”๋”ฉ์œผ๋กœ iOS์™€ Android๋ฅผ ๋™์‹œ์—!

JavaScript๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์„ ๋งŒ๋“œ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ์—ฌ์ •์„ ์‹œ์ž‘ํ•ด๋ณด์ž! ๐ŸŽฏ

๐ŸŒŸ Titanium์ด ๋ญ๊ธธ๋ž˜ ์ด๋ ‡๊ฒŒ ํ•ซํ•œ ๊ฑฐ์•ผ?

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์˜ค๋Š˜์€ ์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•œ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด. ๋ฐ”๋กœ Titanium์ด๋ผ๋Š” ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค๊ณ  ํ•ด! ๐ŸŽ‰

ํ˜น์‹œ ์ด๋Ÿฐ ๊ณ ๋ฏผ ํ•ด๋ณธ ์  ์žˆ์–ด? "์•„, iOS์šฉ ์•ฑ๋„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๊ณ  Android์šฉ ์•ฑ๋„ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š”๋ฐ... ๋‘ ๋ฒˆ ๊ฐœ๋ฐœํ•˜๊ธฐ์—” ์‹œ๊ฐ„๋„ ๋ˆ๋„ ๋ถ€์กฑํ•ด!" ๐Ÿ˜ซ ๋ฐ”๋กœ ์ด๋Ÿฐ ๊ณ ๋ฏผ์„ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๊ฒŒ Titanium์ด์•ผ.

๐Ÿ’ก Titanium์˜ ํ•ต์‹ฌ ์•„์ด๋””์–ด "Write Once, Run Everywhere" - ํ•œ ๋ฒˆ ์ž‘์„ฑํ•˜๋ฉด ์–ด๋””์„œ๋“  ์‹คํ–‰๋œ๋‹ค!

Titanium์€ Appcelerator์—์„œ ๊ฐœ๋ฐœํ•œ ์˜คํ”ˆ์†Œ์Šค ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ์•ผ. ๊ฐ€์žฅ ํฐ ๋งค๋ ฅ์€ JavaScript ํ•˜๋‚˜๋งŒ ์•Œ๋ฉด iOS์™€ Android ์•ฑ์„ ๋™์‹œ์— ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด์ง€! ๐Ÿคฏ

2008๋…„์— ์ฒ˜์Œ ๋“ฑ์žฅํ•œ ์ดํ›„๋กœ ์ˆ˜๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์˜ ์‚ฌ๋ž‘์„ ๋ฐ›์•„์™”์–ด. ํŠนํžˆ ์›น ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ๋Š” ์ •๋ง ๋ฐ˜๊ฐ€์šด ์กด์žฌ์˜€์ง€. ์™œ๋ƒํ•˜๋ฉด ์ด๋ฏธ ์•Œ๊ณ  ์žˆ๋Š” JavaScript ์ง€์‹์„ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ !

JavaScript Titanium SDK iOS App Android App Titanium ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ฐœ๋ฐœ

๐Ÿ”ฅ ์™œ Titanium์„ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ?

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Titanium์˜ ๋งค๋ ฅ์— ๋Œ€ํ•ด ํŒŒํ—ค์ณ๋ณด์ž! ์†”์งํžˆ ๋งํ•ด์„œ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ฐœ๋ฐœ ๋„๊ตฌ๋Š” ์ •๋ง ๋งŽ์•„. React Native, Flutter, Xamarin... ๊ทธ๋Ÿฐ๋ฐ ์™œ ๊ตณ์ด Titanium์ผ๊นŒ? ๐Ÿค”

โœ… Titanium์˜ ์žฅ์ ๋“ค

1. ์ง„์งœ ๋„ค์ดํ‹ฐ๋ธŒ ์„ฑ๋Šฅ
ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•ฑ์ด ์•„๋‹ˆ๋ผ ์‹ค์ œ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋กœ ์ปดํŒŒ์ผ๋ผ!

2. JavaScript ๊ธฐ๋ฐ˜
์›น ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ฐ”๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์–ด!

3. ํ’๋ถ€ํ•œ API
์นด๋ฉ”๋ผ, GPS, ํ‘ธ์‹œ ์•Œ๋ฆผ ๋“ฑ ๋„ค์ดํ‹ฐ๋ธŒ ๊ธฐ๋Šฅ ์™„๋ฒฝ ์ง€์›!

4. ์˜คํ”ˆ์†Œ์Šค
๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ์ปค๋ฎค๋‹ˆํ‹ฐ ์ง€์›๋„ ํ™œ๋ฐœํ•ด!

โš ๏ธ ๊ณ ๋ คํ•ด์•ผ ํ•  ์ ๋“ค

1. ํ•™์Šต ๊ณก์„ 
Titanium๋งŒ์˜ API์™€ ๊ตฌ์กฐ๋ฅผ ์ตํ˜€์•ผ ํ•ด

2. ์•ฑ ํฌ๊ธฐ
๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ๋ณด๋‹ค ์šฉ๋Ÿ‰์ด ํด ์ˆ˜ ์žˆ์–ด

3. ํ”Œ๋žซํผ๋ณ„ ์ฐจ์ด
์™„์ „ํžˆ ๋™์ผํ•˜์ง€๋Š” ์•Š๊ณ  ์•ฝ๊ฐ„์˜ ์กฐ์ •์ด ํ•„์š”ํ•ด

4. ์ตœ์‹  ๊ธฐ๋Šฅ ์ง€์›
์ƒˆ๋กœ์šด OS ๊ธฐ๋Šฅ์€ ์กฐ๊ธˆ ๋Šฆ๊ฒŒ ์ง€์›๋  ์ˆ˜ ์žˆ์–ด

ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๋‹จ์ ๋“ค์„ ๊ณ ๋ คํ•ด๋„ ๊ฐœ๋ฐœ ์‹œ๊ฐ„๊ณผ ๋น„์šฉ์„ ํฌ๊ฒŒ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑด ์ •๋ง ํฐ ๋งค๋ ฅ์ด์•ผ! ํŠนํžˆ ์Šคํƒ€ํŠธ์—…์ด๋‚˜ ์ค‘์†Œ๊ธฐ์—…์—์„œ๋Š” ๋”์šฑ ๊ทธ๋ ‡์ง€. ๐Ÿ’ฐ

๐Ÿ’ก ์žฌ๋Šฅ๋„ท TIP Titanium ๊ฐœ๋ฐœ์„ ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ๊ด€๋ จ ๊ฐ•์˜๋‚˜ ๋ฉ˜ํ† ๋ง์„ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ! ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค๋กœ๋ถ€ํ„ฐ ์ง์ ‘ ๋ฐฐ์šธ ์ˆ˜ ์žˆ๊ฑฐ๋“ .

๐Ÿ› ๏ธ Titanium ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๊ตฌ์ถ•ํ•˜๊ธฐ

์ž, ์ด์ œ ์‹ค์ „์œผ๋กœ ๋“ค์–ด๊ฐ€๋ณด์ž! Titanium์œผ๋กœ ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๋ ค๋ฉด ๋จผ์ € ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด์•ผ ํ•ด. ์ฒ˜์Œ์—๋Š” ์ข€ ๋ณต์žกํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฐจ๊ทผ์ฐจ๊ทผ ๋”ฐ๋ผํ•˜๋ฉด ๊ธˆ๋ฐฉ์ด์•ผ! ๐Ÿ˜Š

1

Node.js ์„ค์น˜

Titanium์€ Node.js ๊ธฐ๋ฐ˜์ด์•ผ. ๋จผ์ € Node.js ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ LTS ๋ฒ„์ „์„ ๋‹ค์šด๋กœ๋“œํ•ด์„œ ์„ค์น˜ํ•˜์ž!

2

Titanium CLI ์„ค์น˜

ํ„ฐ๋ฏธ๋„์ด๋‚˜ ๋ช…๋ น ํ”„๋กฌํ”„ํŠธ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด:

npm install -g titanium alloy
3

ํ”Œ๋žซํผ๋ณ„ SDK ์„ค์น˜

iOS ๊ฐœ๋ฐœ์šฉ: Xcode (Mac์—์„œ๋งŒ ๊ฐ€๋Šฅ)
Android ๊ฐœ๋ฐœ์šฉ: Android Studio์™€ Android SDK

4

Titanium SDK ์„ค์น˜

๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ์ตœ์‹  Titanium SDK๋ฅผ ์„ค์น˜ํ•˜์ž:

ti sdk install latest

์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ti info ๋ช…๋ น์–ด๋กœ ๋ชจ๋“  ๊ฒŒ ์ œ๋Œ€๋กœ ์„ค์น˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด. ์ดˆ๋ก์ƒ‰ ์ฒดํฌ ๋งˆํฌ๋“ค์ด ๋œจ๋ฉด ์„ฑ๊ณต! โœ…

๐Ÿ”ง ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ํŒ ์ฒ˜์Œ ์„ค์น˜ํ•  ๋•Œ๋Š” ์‹œ๊ฐ„์ด ์ข€ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ Android SDK ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋‹ˆ๊นŒ ์ปคํ”ผ ํ•œ ์ž” ๋งˆ์‹œ๋ฉด์„œ ๊ธฐ๋‹ค๋ ค! โ˜•

๐ŸŽฏ ์ฒซ ๋ฒˆ์งธ Titanium ์•ฑ ๋งŒ๋“ค๊ธฐ

๋“œ๋””์–ด ์ฒซ ๋ฒˆ์งธ ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณผ ์‹œ๊ฐ„์ด์•ผ! ์ „ํ†ต์ ์ธ "Hello World" ์•ฑ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด์ž. ๊ฐ„๋‹จํ•ด ๋ณด์ด์ง€๋งŒ Titanium์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์„ ๋ชจ๋‘ ๋‹ด๊ณ  ์žˆ์–ด! ๐ŸŒ

๐Ÿ“ฑ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์„œ ์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž:

ti create --type app --name MyFirstApp --id com.example.myfirstapp --platform ios,android

์ด ๋ช…๋ น์–ด๊ฐ€ ์‹คํ–‰๋˜๋ฉด MyFirstApp์ด๋ผ๋Š” ํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ๊ทธ ์•ˆ์— ๊ธฐ๋ณธ์ ์ธ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๊ฐ€ ๋งŒ๋“ค์–ด์ ธ! ๐Ÿ“

๐Ÿ—๏ธ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ

์ƒ์„ฑ๋œ ํ”„๋กœ์ ํŠธ ํด๋”๋ฅผ ์—ด์–ด๋ณด๋ฉด ์ด๋Ÿฐ ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์–ด:

MyFirstApp/
โ”œโ”€โ”€ app/
โ”‚ โ”œโ”€โ”€ controllers/
โ”‚ โ”œโ”€โ”€ models/
โ”‚ โ”œโ”€โ”€ styles/
โ”‚ โ”œโ”€โ”€ views/
โ”‚ โ””โ”€โ”€ alloy.js
โ”œโ”€โ”€ Resources/
โ”œโ”€โ”€ platform/
โ”œโ”€โ”€ plugins/
โ””โ”€โ”€ tiapp.xml

๐Ÿ“‚ app/controllers

์•ฑ์˜ ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋Š” JavaScript ํŒŒ์ผ๋“ค์ด ๋“ค์–ด๊ฐ€๋Š” ๊ณณ์ด์•ผ

๐Ÿ“‚ app/views

UI ๋ ˆ์ด์•„์›ƒ์„ ์ •์˜ํ•˜๋Š” XML ํŒŒ์ผ๋“ค์˜ ์ง‘ํ•ฉ์†Œ

๐Ÿ“‚ app/styles

์Šคํƒ€์ผ๋ง์„ ๋‹ด๋‹นํ•˜๋Š” TSS(Titanium Style Sheet) ํŒŒ์ผ๋“ค

๐Ÿ“„ tiapp.xml

์•ฑ์˜ ์„ค์ •๊ณผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํ•ต์‹ฌ ํŒŒ์ผ

โœจ ์ฒซ ๋ฒˆ์งธ ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ

์ด์ œ ์‹ค์ œ๋กœ ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด๋ณด์ž! app/views/index.xml ํŒŒ์ผ์„ ์—ด์–ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด๋ด:

<Alloy>
    <Window class="container">
        <View class="header">
            <Label class="title">๐ŸŽ‰ ๋‚ด ์ฒซ Titanium ์•ฑ!</Label>
        </View>
        
        <View class="content">
            <Button class="myButton" onClick="sayHello">์•ˆ๋…•ํ•˜์„ธ์š”!</Button>
            <Label class="message" id="messageLabel"></Label>
        </View>
    </Window>
</Alloy>

๊ทธ๋ฆฌ๊ณ  app/controllers/index.js์—์„œ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ด๋ณด์ž:

function sayHello() {
    var messages = [
        "์•ˆ๋…•ํ•˜์„ธ์š”! ๐Ÿ‘‹",
        "Titanium ์ตœ๊ณ ! ๐Ÿš€", 
        "ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ฐœ๋ฐœ ๋งŒ์„ธ! ๐ŸŽŠ",
        "JavaScript๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์„! ๐Ÿ’ช"
    ];
    
    var randomMessage = messages[Math.floor(Math.random() * messages.length)];
    $.messageLabel.text = randomMessage;
}

$.index.open();

๋งˆ์ง€๋ง‰์œผ๋กœ app/styles/index.tss์—์„œ ์Šคํƒ€์ผ์„ ์˜ˆ์˜๊ฒŒ ๊พธ๋ฉฐ๋ณด์ž:

".container": {
    backgroundColor: "#f0f0f0"
}

".header": {
    backgroundColor: "#667eea",
    height: "100dp",
    width: Ti.UI.FILL
}

".title": {
    text: "๋‚ด ์ฒซ Titanium ์•ฑ!",
    color: "white",
    font: {
        fontSize: "24dp",
        fontWeight: "bold"
    }
}

".content": {
    layout: "vertical",
    top: "20dp"
}

".myButton": {
    backgroundColor: "#764ba2",
    color: "white",
    borderRadius: "10dp",
    width: "200dp",
    height: "50dp",
    top: "20dp"
}

".message": {
    top: "30dp",
    font: {
        fontSize: "18dp"
    },
    textAlign: "center"
}

๐Ÿƒโ€โ™‚๏ธ ์•ฑ ์‹คํ–‰ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ

์ฝ”๋“œ๋ฅผ ๋‹ค ์ž‘์„ฑํ–ˆ์œผ๋‹ˆ ์ด์ œ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•ด๋ณด์ž! ์ด ์ˆœ๊ฐ„์ด ๊ฐ€์žฅ ์„ค๋ ˆ๋Š” ์ˆœ๊ฐ„์ด์•ผ. ๊ณผ์—ฐ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์•ฑ์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ• ๊นŒ? ๐Ÿ˜

๐Ÿ“ฑ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์‹คํ–‰

iOS ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์‹คํ–‰:

ti build -p ios -T simulator

Android ์—๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์‹คํ–‰:

ti build -p android -T emulator

๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด Titanium์ด ์šฐ๋ฆฌ์˜ JavaScript ์ฝ”๋“œ๋ฅผ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๊ฐ ํ”Œ๋žซํผ์— ๋งž๋Š” ์•ฑ์„ ๋นŒ๋“œํ•ด์ค˜. ์ด ๊ณผ์ •์ด ๋ฐ”๋กœ Titanium์˜ ๋งˆ๋ฒ•์ด์•ผ! โœจ

๐ŸŽฏ ๋นŒ๋“œ ๊ณผ์ •์—์„œ ์ผ์–ด๋‚˜๋Š” ์ผ๋“ค 1. JavaScript ์ฝ”๋“œ ๋ถ„์„ ๋ฐ ์ตœ์ ํ™”
2. ํ”Œ๋žซํผ๋ณ„ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ ์ƒ์„ฑ
3. ๋ฆฌ์†Œ์Šค ํŒŒ์ผ ์ฒ˜๋ฆฌ ๋ฐ ํŒจํ‚ค์ง•
4. ์ตœ์ข… ์•ฑ ํŒŒ์ผ(.ipa ๋˜๋Š” .apk) ์ƒ์„ฑ

๐Ÿ” ๋””๋ฒ„๊น…๊ณผ ๋กœ๊ทธ ํ™•์ธ

๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด ๋‹น์—ฐํžˆ ๋ฒ„๊ทธ๋„ ์ƒ๊ธฐ๊ณ  ๋ฌธ์ œ๋„ ๋ฐœ์ƒํ•ด. ์ด๋•Œ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋‘๋Š” ๊ฒŒ ์ •๋ง ์ค‘์š”ํ•ด!

// ์ฝ˜์†”์— ๋กœ๊ทธ ์ถœ๋ ฅ
Ti.API.info("์ด๊ฒƒ์€ ์ •๋ณด ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค");
Ti.API.warn("์ด๊ฒƒ์€ ๊ฒฝ๊ณ  ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค");
Ti.API.error("์ด๊ฒƒ์€ ์—๋Ÿฌ ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค");

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•œ ๋กœ๊ทธ๋“ค์€ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋‚˜ ์—๋ฎฌ๋ ˆ์ดํ„ฐ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ํ„ฐ๋ฏธ๋„์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด. ๋””๋ฒ„๊น…ํ•  ๋•Œ ์ •๋ง ์œ ์šฉํ•˜๋‹ˆ๊นŒ ๊ผญ ๊ธฐ์–ตํ•ด๋‘ฌ! ๐Ÿ›

๐ŸŽจ Alloy MVC ํŒจํ„ด ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

Titanium์˜ ์ง„์งœ ํŒŒ์›Œ๋Š” Alloy๋ผ๋Š” MVC ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ๋‚˜์™€! Alloy๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ํ›จ์”ฌ ์ฒด๊ณ„์ ์ด๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ—๏ธ

๐ŸŽญ MVC๊ฐ€ ๋ญ์•ผ?

MVC๋Š” Model-View-Controller์˜ ์ค„์ž„๋ง์ด์•ผ. ๊ฐ๊ฐ์˜ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํŒจํ„ด์ด์ง€:

๐Ÿ“Š Model (๋ชจ๋ธ)

๋ฐ์ดํ„ฐ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋‹ด๋‹นํ•ด. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™, API ํ˜ธ์ถœ ๋“ฑ์˜ ์ž‘์—…์„ ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌํ•ด!

๐Ÿ‘๏ธ View (๋ทฐ)

์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ํ™”๋ฉด์„ ๋‹ด๋‹นํ•ด. XML๋กœ ์ž‘์„ฑํ•˜๋ฉฐ, ๋ฒ„ํŠผ, ๋ผ๋ฒจ, ์ด๋ฏธ์ง€ ๋“ฑ์˜ UI ์š”์†Œ๋“ค์„ ๋ฐฐ์น˜ํ•ด!

๐ŸŽฎ Controller (์ปจํŠธ๋กค๋Ÿฌ)

Model๊ณผ View๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋‹ค๋ฆฌ ์—ญํ• ์„ ํ•ด. ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ๋ฐ›์•„์„œ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•ด!

๐Ÿ“ ์‹ค์ œ MVC ์˜ˆ์ œ ๋งŒ๋“ค๊ธฐ

๊ฐ„๋‹จํ•œ ํ•  ์ผ ๋ชฉ๋ก ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ MVC ํŒจํ„ด์„ ์ดํ•ดํ•ด๋ณด์ž! ๐Ÿ—’๏ธ

1. Model ๋งŒ๋“ค๊ธฐ (app/models/todo.js):

exports.definition = {
    config: {
        columns: {
            "id": "INTEGER PRIMARY KEY AUTOINCREMENT",
            "title": "TEXT",
            "completed": "INTEGER DEFAULT 0",
            "created_at": "TEXT"
        },
        adapter: {
            type: "sql",
            collection_name: "todos"
        }
    },
    
    extendModel: function(Model) {
        _.extend(Model.prototype, {
            // ํ•  ์ผ ์™„๋ฃŒ ์ƒํƒœ ํ† ๊ธ€
            toggleComplete: function() {
                this.set('completed', this.get('completed') ? 0 : 1);
                this.save();
            }
        });
        
        return Model;
    }
};

2. View ๋งŒ๋“ค๊ธฐ (app/views/todos.xml):

<Alloy>
    <Window class="container">
        <View class="header">
            <Label class="headerTitle">๐Ÿ“ ํ•  ์ผ ๋ชฉ๋ก</Label>
            <Button class="addButton" onClick="showAddDialog">โž•</Button>
        </View>
        
        <TableView id="todoTable" class="todoTable">
        </TableView>
    </Window>
</Alloy>

3. Controller ๋งŒ๋“ค๊ธฐ (app/controllers/todos.js):

var todos = Alloy.Collections.todo;

function loadTodos() {
    todos.fetch();
    var tableData = [];
    
    todos.each(function(todo) {
        var row = Ti.UI.createTableViewRow({
            title: todo.get('title'),
            hasChild: false,
            color: todo.get('completed') ? '#999' : '#333',
            font: {
                strikethrough: todo.get('completed')
            }
        });
        
        row.addEventListener('click', function() {
            todo.toggleComplete();
            loadTodos(); // ํ™”๋ฉด ์ƒˆ๋กœ๊ณ ์นจ
        });
        
        tableData.push(row);
    });
    
    $.todoTable.setData(tableData);
}

function showAddDialog() {
    var dialog = Ti.UI.createAlertDialog({
        title: '์ƒˆ ํ•  ์ผ ์ถ”๊ฐ€',
        message: 'ํ•  ์ผ์„ ์ž…๋ ฅํ•˜์„ธ์š”:',
        style: Ti.UI.iPhone.AlertDialogStyle.PLAIN_TEXT_INPUT,
        buttonNames: ['์ทจ์†Œ', '์ถ”๊ฐ€']
    });
    
    dialog.addEventListener('click', function(e) {
        if (e.index === 1) { // '์ถ”๊ฐ€' ๋ฒ„ํŠผ
            var newTodo = Alloy.createModel('todo', {
                title: e.text,
                created_at: new Date().toISOString()
            });
            newTodo.save();
            todos.add(newTodo);
            loadTodos();
        }
    });
    
    dialog.show();
}

// ์•ฑ ์‹œ์ž‘์‹œ ํ•  ์ผ ๋ชฉ๋ก ๋กœ๋“œ
loadTodos();

$.todos.open();

์ด๋ ‡๊ฒŒ MVC ํŒจํ„ด์œผ๋กœ ๋‚˜๋ˆ„๋‹ˆ๊นŒ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›Œ์กŒ์ง€? ๊ฐ๊ฐ์˜ ์—ญํ• ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด์„œ ๋‚˜์ค‘์— ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋„ ํ›จ์”ฌ ํŽธํ•ด! ๐ŸŽฏ

๐ŸŒ ๋„ค์ดํ‹ฐ๋ธŒ API ํ™œ์šฉํ•˜๊ธฐ

Titanium์˜ ์ง„์งœ ๋งค๋ ฅ์€ ๋„ค์ดํ‹ฐ๋ธŒ API๋ฅผ JavaScript๋กœ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด์•ผ! ์นด๋ฉ”๋ผ, GPS, ํ‘ธ์‹œ ์•Œ๋ฆผ ๋“ฑ ์Šค๋งˆํŠธํฐ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ”ฅ

๐Ÿ“ธ ์นด๋ฉ”๋ผ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

์‚ฌ์ง„์„ ์ฐ๊ณ  ๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณด์ž:

function openCamera() {
    // ์นด๋ฉ”๋ผ ๊ถŒํ•œ ํ™•์ธ
    if (!Ti.Media.hasCameraPermissions()) {
        Ti.Media.requestCameraPermissions(function(e) {
            if (e.success) {
                showCameraOptions();
            } else {
                alert('์นด๋ฉ”๋ผ ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค!');
            }
        });
    } else {
        showCameraOptions();
    }
}

function showCameraOptions() {
    var dialog = Ti.UI.createOptionDialog({
        title: '์‚ฌ์ง„ ์„ ํƒ',
        options: ['์นด๋ฉ”๋ผ๋กœ ์ดฌ์˜', '๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ์„ ํƒ', '์ทจ์†Œ'],
        cancel: 2
    });
    
    dialog.addEventListener('click', function(e) {
        if (e.index === 0) {
            // ์นด๋ฉ”๋ผ๋กœ ์ดฌ์˜
            Ti.Media.showCamera({
                success: function(event) {
                    var image = event.media;
                    // ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋กœ์ง
                    displayImage(image);
                },
                error: function(error) {
                    alert('์‚ฌ์ง„ ์ดฌ์˜์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: ' + error.error);
                },
                mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO]
            });
        } else if (e.index === 1) {
            // ๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ์„ ํƒ
            Ti.Media.openPhotoGallery({
                success: function(event) {
                    var image = event.media;
                    displayImage(image);
                },
                error: function(error) {
                    alert('์ด๋ฏธ์ง€ ์„ ํƒ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: ' + error.error);
                }
            });
        }
    });
    
    dialog.show();
}

๐Ÿ“ ์œ„์น˜ ์ •๋ณด ํ™œ์šฉํ•˜๊ธฐ

GPS๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ธฐ๋Šฅ๋„ ๋งŒ๋“ค์–ด๋ณด์ž:

function getCurrentLocation() {
    // ์œ„์น˜ ๊ถŒํ•œ ํ™•์ธ
    if (!Ti.Geolocation.hasLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE)) {
        Ti.Geolocation.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE, function(e) {
            if (e.success) {
                getLocation();
            } else {
                alert('์œ„์น˜ ๊ถŒํ•œ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค!');
            }
        });
    } else {
        getLocation();
    }
}

function getLocation() {
    Ti.Geolocation.getCurrentPosition(function(e) {
        if (e.error) {
            alert('์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: ' + e.error);
        } else {
            var latitude = e.coords.latitude;
            var longitude = e.coords.longitude;
            
            // ์œ„์น˜ ์ •๋ณด๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œ
            $.locationLabel.text = `๐Ÿ“ ํ˜„์žฌ ์œ„์น˜: ${latitude.toFixed(4)}, ${longitude.toFixed(4)}`;
            
            // ์—ญ์ง€์˜ค์ฝ”๋”ฉ์œผ๋กœ ์ฃผ์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ
            Ti.Geolocation.reverseGeocoder(latitude, longitude, function(e) {
                if (e.success && e.places && e.places.length > 0) {
                    var place = e.places[0];
                    $.addressLabel.text = `๐Ÿ  ์ฃผ์†Œ: ${place.address}`;
                }
            });
        }
    });
}
๐Ÿ” ๊ถŒํ•œ ๊ด€๋ฆฌ ํŒ ๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ๋ฅผ ์œ„ํ•ด ์นด๋ฉ”๋ผ, ์œ„์น˜, ์—ฐ๋ฝ์ฒ˜ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐ˜๋“œ์‹œ ๊ถŒํ•œ์„ ์š”์ฒญํ•ด์•ผ ํ•ด. Titanium์—์„œ๋Š” ์ด๋Ÿฐ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด!

๐Ÿ”” ํ‘ธ์‹œ ์•Œ๋ฆผ ๊ตฌํ˜„ํ•˜๊ธฐ

ํ‘ธ์‹œ ์•Œ๋ฆผ์€ ๋ชจ๋ฐ”์ผ ์•ฑ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์•ผ. ์‚ฌ์šฉ์ž์™€์˜ ์†Œํ†ต์„ ์œ„ํ•ด์„œ๋Š” ํ•„์ˆ˜์ ์ด์ง€!

// ํ‘ธ์‹œ ์•Œ๋ฆผ ๋“ฑ๋ก
function registerForPushNotifications() {
    if (Ti.Platform.name === 'iPhone OS') {
        // iOS ํ‘ธ์‹œ ์•Œ๋ฆผ ๋“ฑ๋ก
        Ti.Network.registerForPushNotifications({
            types: [
                Ti.Network.NOTIFICATION_TYPE_BADGE,
                Ti.Network.NOTIFICATION_TYPE_ALERT,
                Ti.Network.NOTIFICATION_TYPE_SOUND
            ],
            success: function(e) {
                var deviceToken = e.deviceToken;
                Ti.API.info('ํ‘ธ์‹œ ํ† ํฐ: ' + deviceToken);
                // ์„œ๋ฒ„์— ํ† ํฐ ์ „์†ก
                sendTokenToServer(deviceToken);
            },
            error: function(e) {
                Ti.API.error('ํ‘ธ์‹œ ๋“ฑ๋ก ์‹คํŒจ: ' + e.error);
            },
            callback: function(e) {
                // ํ‘ธ์‹œ ์•Œ๋ฆผ์„ ๋ฐ›์•˜์„ ๋•Œ ์ฒ˜๋ฆฌ
                Ti.API.info('ํ‘ธ์‹œ ์•Œ๋ฆผ ์ˆ˜์‹ : ' + JSON.stringify(e));
            }
        });
    } else {
        // Android ํ‘ธ์‹œ ์•Œ๋ฆผ์€ Firebase Cloud Messaging ์‚ฌ์šฉ
        var CloudPush = require('ti.cloudpush');
        CloudPush.retrieveDeviceToken({
            success: function(e) {
                var deviceToken = e.deviceToken;
                sendTokenToServer(deviceToken);
            },
            error: function(e) {
                Ti.API.error('ํ‘ธ์‹œ ํ† ํฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ: ' + e.error);
            }
        });
    }
}

๐ŸŽฏ ํ”Œ๋žซํผ๋ณ„ ์ตœ์ ํ™” ์ „๋žต

ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ฐœ๋ฐœ์˜ ๋ชฉํ‘œ๋Š” "ํ•œ ๋ฒˆ ์ž‘์„ฑ, ์–ด๋””์„œ๋“  ์‹คํ–‰"์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ๊ฐ ํ”Œ๋žซํผ์˜ ํŠน์„ฑ์„ ๊ณ ๋ คํ•œ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•ด. iOS์™€ Android๋Š” UI/UX ๊ฐ€์ด๋“œ๋ผ์ธ์ด ๋‹ค๋ฅด๊ฑฐ๋“ ! ๐Ÿ“ฑ

๐ŸŽ iOS vs ๐Ÿค– Android ์ฐจ์ด์ 

๐ŸŽ iOS ํŠน์ง•

โ€ข ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ์Šคํƒ€์ผ
โ€ข ํƒญ ๋ฐ” ์œ„์น˜ (ํ•˜๋‹จ)
โ€ข ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜
โ€ข Human Interface Guidelines

๐Ÿค– Android ํŠน์ง•

โ€ข ์•ก์…˜ ๋ฐ” ์Šคํƒ€์ผ
โ€ข ํ•˜๋“œ์›จ์–ด ๋ฐฑ ๋ฒ„ํŠผ
โ€ข ๋จธํ‹ฐ๋ฆฌ์–ผ ๋””์ž์ธ
โ€ข ๋‹ค์–‘ํ•œ ํ™”๋ฉด ํฌ๊ธฐ

๐Ÿ”ง ํ”Œ๋žซํผ๋ณ„ ์ฝ”๋“œ ๋ถ„๊ธฐ

Titanium์—์„œ๋Š” ํ”Œ๋žซํผ์„ ๊ฐ์ง€ํ•ด์„œ ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด:

// ํ”Œ๋žซํผ ๊ฐ์ง€
if (Ti.Platform.osname === 'iphone' || Ti.Platform.osname === 'ipad') {
    // iOS ์ „์šฉ ์ฝ”๋“œ
    var navWindow = Ti.UI.iOS.createNavigationWindow({
        window: $.index
    });
    navWindow.open();
} else if (Ti.Platform.osname === 'android') {
    // Android ์ „์šฉ ์ฝ”๋“œ
    $.index.activity.actionBar.title = "๋‚ด ์•ฑ";
    $.index.activity.actionBar.displayHomeAsUp = true;
    $.index.open();
}

// ํ™”๋ฉด ํฌ๊ธฐ์— ๋”ฐ๋ฅธ ๋ฐ˜์‘ํ˜• ๋””์ž์ธ
var screenWidth = Ti.Platform.displayCaps.platformWidth;
var screenHeight = Ti.Platform.displayCaps.platformHeight;

if (screenWidth > 600) {
    // ํƒœ๋ธ”๋ฆฟ์šฉ ๋ ˆ์ด์•„์›ƒ
    $.mainView.layout = 'horizontal';
} else {
    // ์Šค๋งˆํŠธํฐ์šฉ ๋ ˆ์ด์•„์›ƒ
    $.mainView.layout = 'vertical';
}

๐ŸŽจ ํ”Œ๋žซํผ๋ณ„ ์Šคํƒ€์ผ๋ง

TSS ํŒŒ์ผ์—์„œ๋„ ํ”Œ๋žซํผ๋ณ„๋กœ ๋‹ค๋ฅธ ์Šคํƒ€์ผ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

// ๊ณตํ†ต ์Šคํƒ€์ผ
".myButton": {
    width: "200dp",
    height: "50dp",
    borderRadius: "8dp"
}

// iOS ์ „์šฉ ์Šคํƒ€์ผ
".myButton[platform=ios]": {
    backgroundColor: "#007AFF",
    color: "white"
}

// Android ์ „์šฉ ์Šคํƒ€์ผ
".myButton[platform=android]": {
    backgroundColor: "#4CAF50",
    color: "white",
    elevation: 4
}
๐ŸŽจ ๋””์ž์ธ ์ผ๊ด€์„ฑ vs ํ”Œ๋žซํผ ํŠน์„ฑ ์™„์ „ํžˆ ๋™์ผํ•œ ๋””์ž์ธ์„ ๊ณ ์ง‘ํ•˜๊ธฐ๋ณด๋‹ค๋Š”, ๊ฐ ํ”Œ๋žซํผ ์‚ฌ์šฉ์ž๋“ค์ด ์ต์ˆ™ํ•œ UI ํŒจํ„ด์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ธก๋ฉด์—์„œ ๋” ์ข‹์•„!

๐Ÿš€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋น„๋ฒ•

์•ฑ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ์„ฑ๋Šฅ ์ข‹์€ ์•ฑ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ•ด! ์‚ฌ์šฉ์ž๋“ค์€ ๋А๋ฆฐ ์•ฑ์„ ์ฐธ์ง€ ๋ชปํ•˜๊ฑฐ๋“ . ๋ช‡ ๊ฐ€์ง€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ! โšก

๐Ÿ’พ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

JavaScript๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ์žˆ์ง€๋งŒ, ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ํŠนํžˆ ์ค‘์š”ํ•ด:

// โŒ ๋‚˜์œ ์˜ˆ: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ€๋Šฅ์„ฑ
var images = [];
for (var i = 0; i < 100; i++) {
    images.push(Ti.UI.createImageView({
        image: 'large_image_' + i + '.jpg'
    }));
}

// โœ… ์ข‹์€ ์˜ˆ: ํ•„์š”ํ•  ๋•Œ๋งŒ ์ƒ์„ฑํ•˜๊ณ  ์ •๋ฆฌ
function createImageView(index) {
    var imageView = Ti.UI.createImageView({
        image: 'large_image_' + index + '.jpg'
    });
    
    // ์‚ฌ์šฉ ํ›„ ์ •๋ฆฌ
    imageView.addEventListener('postlayout', function() {
        // ์ด๋ฏธ์ง€ ๋กœ๋“œ ์™„๋ฃŒ ํ›„ ์ฒ˜๋ฆฌ
    });
    
    return imageView;
}

// ๋ทฐ๊ฐ€ ๋‹ซํž ๋•Œ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
$.window.addEventListener('close', function() {
    // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ œ๊ฑฐ
    // ํƒ€์ด๋จธ ์ •๋ฆฌ
    // ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ฐธ์กฐ ํ•ด์ œ
});

๐ŸŽ๏ธ UI ๋ Œ๋”๋ง ์ตœ์ ํ™”

๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค๊ณผ ๋น ๋ฅธ ํ™”๋ฉด ์ „ํ™˜์„ ์œ„ํ•œ ํŒ๋“ค์ด์•ผ:

๐ŸŽญ ๋ทฐ ์žฌ์‚ฌ์šฉ

TableView๋‚˜ ListView์—์„œ๋Š” ๋ทฐ ์žฌ์‚ฌ์šฉ์„ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๊ณ  ์Šคํฌ๋กค ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œ์ผœ!

๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ ์ ˆํžˆ ์กฐ์ ˆํ•˜๊ณ , ํ•„์š”์— ๋”ฐ๋ผ ์••์ถ•์„ ์‚ฌ์šฉํ•ด. ํŠนํžˆ ๊ณ ํ•ด์ƒ๋„ ์ด๋ฏธ์ง€๋Š” ์ฃผ์˜!

โšก ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด๋‚˜ ๋ฌด๊ฑฐ์šด ์ž‘์—…์€ ๋ฐ˜๋“œ์‹œ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•ด์„œ UI๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๋„๋ก ํ•ด!

๐ŸŽฏ ๋ ˆ์ด์•„์›ƒ ์ตœ์ ํ™”

๋ณต์žกํ•œ ์ค‘์ฒฉ ๋ ˆ์ด์•„์›ƒ๋ณด๋‹ค๋Š” ๋‹จ์ˆœํ•˜๊ณ  ํšจ์œจ์ ์ธ ๋ ˆ์ด์•„์›ƒ์„ ์‚ฌ์šฉํ•ด!

๐Ÿ“Š ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง

์•ฑ์˜ ์„ฑ๋Šฅ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์•Œ์•„๋‘์ž:

// ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฒดํฌ
function checkMemoryUsage() {
    var memInfo = Ti.Platform.availableMemory;
    Ti.API.info('์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ: ' + memInfo + 'MB');
    
    if (memInfo < 50) {
        Ti.API.warn('๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ๊ฒฝ๊ณ !');
        // ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
        cleanupResources();
    }
}

// ์•ฑ ์„ฑ๋Šฅ ์ง€ํ‘œ ์ˆ˜์ง‘
function trackPerformance(actionName, startTime) {
    var endTime = new Date().getTime();
    var duration = endTime - startTime;
    
    Ti.API.info(`${actionName} ์‹คํ–‰ ์‹œ๊ฐ„: ${duration}ms`);
    
    // ์„ฑ๋Šฅ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ์ „์†ก (์„ ํƒ์‚ฌํ•ญ)
    if (duration > 1000) {
        Ti.API.warn(`${actionName}์ด ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค: ${duration}ms`);
    }
}

๐Ÿ”— ์„œ๋ฒ„์™€์˜ ๋ฐ์ดํ„ฐ ํ†ต์‹ 

์š”์ฆ˜ ์•ฑ๋“ค์€ ๋Œ€๋ถ€๋ถ„ ์„œ๋ฒ„์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์•„์•ผ ํ•ด. REST API ํ˜ธ์ถœ, JSON ํŒŒ์‹ฑ, ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊นŒ์ง€... ์ด ๋ชจ๋“  ๊ฑธ Titanium์—์„œ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž! ๐ŸŒ

๐Ÿ“ก HTTP ์š”์ฒญ ๋ณด๋‚ด๊ธฐ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ GET ์š”์ฒญ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด์ž:

function fetchUserData(userId) {
    var xhr = Ti.Network.createHTTPClient({
        onload: function(e) {
            try {
                var response = JSON.parse(this.responseText);
                displayUserInfo(response);
            } catch (error) {
                Ti.API.error('JSON ํŒŒ์‹ฑ ์—๋Ÿฌ: ' + error.message);
                showErrorMessage('๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
            }
        },
        onerror: function(e) {
            Ti.API.error('๋„คํŠธ์›Œํฌ ์—๋Ÿฌ: ' + e.error);
            showErrorMessage('๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.');
        },
        timeout: 10000 // 10์ดˆ ํƒ€์ž„์•„์›ƒ
    });
    
    xhr.open('GET', `https://api.example.com/users/${userId}`);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.setRequestHeader('Authorization', 'Bearer ' + getAuthToken());
    xhr.send();
}

๐Ÿ“ค POST ์š”์ฒญ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก

์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์ง€:

function createNewPost(title, content) {
    var postData = {
        title: title,
        content: content,
        author_id: getCurrentUserId(),
        created_at: new Date().toISOString()
    };
    
    var xhr = Ti.Network.createHTTPClient({
        onload: function(e) {
            if (this.status === 201) {
                // ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋จ
                var newPost = JSON.parse(this.responseText);
                showSuccessMessage('๊ฒŒ์‹œ๊ธ€์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! ๐ŸŽ‰');
                navigateToPost(newPost.id);
            } else {
                showErrorMessage('๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
            }
        },
        onerror: function(e) {
            Ti.API.error('๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ ์—๋Ÿฌ: ' + e.error);
            showErrorMessage('๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋กœ ๊ฒŒ์‹œ๊ธ€์„ ์ž‘์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.');
        }
    });
    
    xhr.open('POST', 'https://api.example.com/posts');
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.send(JSON.stringify(postData));
}

๐Ÿ”„ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”

์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋™๊ธฐํ™”ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” WebSocket์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

var socket;

function connectWebSocket() {
    socket = Ti.Network.Socket.createTCP({
        host: 'ws.example.com',
        port: 8080,
        connected: function(e) {
            Ti.API.info('WebSocket ์—ฐ๊ฒฐ ์„ฑ๊ณต!');
            // ์—ฐ๊ฒฐ ์„ฑ๊ณต ์‹œ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์š”์ฒญ
            socket.write(Ti.createBuffer({
                value: JSON.stringify({type: 'subscribe', channel: 'updates'})
            }));
        },
        error: function(e) {
            Ti.API.error('WebSocket ์—ฐ๊ฒฐ ์‹คํŒจ: ' + e.error);
        }
    });
    
    socket.addEventListener('read', function(e) {
        var data = e.buffer.toString();
        try {
            var message = JSON.parse(data);
            handleRealtimeUpdate(message);
        } catch (error) {
            Ti.API.error('WebSocket ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ์—๋Ÿฌ: ' + error.message);
        }
    });
    
    socket.connect();
}

function handleRealtimeUpdate(message) {
    switch (message.type) {
        case 'new_message':
            addNewMessageToUI(message.data);
            break;
        case 'user_online':
            updateUserStatus(message.user_id, 'online');
            break;
        case 'notification':
            showNotification(message.title, message.body);
            break;
    }
}
๐ŸŒ API ํ†ต์‹  ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค โ€ข ํ•ญ์ƒ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ํฌํ•จํ•ด
โ€ข ํƒ€์ž„์•„์›ƒ์„ ์„ค์ •ํ•ด
โ€ข ๋กœ๋”ฉ ์ธ๋””์ผ€์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค˜
โ€ข ์˜คํ”„๋ผ์ธ ์ƒํ™ฉ๋„ ๊ณ ๋ คํ•ด
โ€ข ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋Š” HTTPS๋ฅผ ์‚ฌ์šฉํ•ด

๐Ÿ—„๏ธ ๋กœ์ปฌ ๋ฐ์ดํ„ฐ ์ €์žฅ๊ณผ ๊ด€๋ฆฌ

๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์—๋งŒ ์˜์กดํ•  ์ˆ˜๋Š” ์—†์–ด. ์˜คํ”„๋ผ์ธ ์ƒํ™ฉ์ด๋‚˜ ๋น ๋ฅธ ์ ‘๊ทผ์„ ์œ„ํ•ด์„œ๋Š” ๋กœ์ปฌ ์ €์žฅ์†Œ๋ฅผ ํ™œ์šฉํ•ด์•ผ ํ•ด! Titanium์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‹ค์–‘ํ•œ ์ €์žฅ ๋ฐฉ๋ฒ•๋“ค์„ ์•Œ์•„๋ณด์ž. ๐Ÿ’พ

๐Ÿช Properties (๊ฐ„๋‹จํ•œ ์„ค์ •๊ฐ’ ์ €์žฅ)

์‚ฌ์šฉ์ž ์„ค์ •์ด๋‚˜ ๊ฐ„๋‹จํ•œ ๊ฐ’๋“ค์„ ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•ด:

// ๋ฐ์ดํ„ฐ ์ €์žฅ
Ti.App.Properties.setString('username', 'john_doe');
Ti.App.Properties.setBool('notifications_enabled', true);
Ti.App.Properties.setInt('app_launch_count', 5);

// ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ
var username = Ti.App.Properties.getString('username', ''); // ๊ธฐ๋ณธ๊ฐ’: ๋นˆ ๋ฌธ์ž์—ด
var notificationsEnabled = Ti.App.Properties.getBool('notifications_enabled', true);
var launchCount = Ti.App.Properties.getInt('app_launch_count', 0);

// ์‚ฌ์šฉ ์˜ˆ์ œ: ์•ฑ ์‹คํ–‰ ํšŸ์ˆ˜ ์ถ”์ 
function trackAppLaunch() {
    var currentCount = Ti.App.Properties.getInt('app_launch_count', 0);
    Ti.App.Properties.setInt('app_launch_count', currentCount + 1);
    
    if (currentCount === 0) {
        showWelcomeMessage();
    } else if (currentCount === 10) {
        showRatingDialog(); // 10๋ฒˆ์งธ ์‹คํ–‰ ์‹œ ํ‰์  ์š”์ฒญ
    }
}

๐Ÿ—ƒ๏ธ SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ๋‚˜ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ๋Š” SQLite๋ฅผ ์‚ฌ์šฉํ•ด:

// ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ
var db = Ti.Database.open('myapp_db');

// ํ…Œ์ด๋ธ” ์ƒ์„ฑ
db.execute(`
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )
`);

// ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
function addUser(name, email) {
    try {
        db.execute('INSERT INTO users (name, email) VALUES (?, ?)', name, email);
        Ti.API.info('์‚ฌ์šฉ์ž ์ถ”๊ฐ€ ์„ฑ๊ณต!');
    } catch (error) {
        Ti.API.error('์‚ฌ์šฉ์ž ์ถ”๊ฐ€ ์‹คํŒจ: ' + error.message);
    }
}

// ๋ฐ์ดํ„ฐ ์กฐํšŒ
function getUsers() {
    var users = [];
    var rows = db.execute('SELECT * FROM users ORDER BY created_at DESC');
    
    while (rows.isValidRow()) {
        users.push({
            id: rows.fieldByName('id'),
            name: rows.fieldByName('name'),
            email: rows.fieldByName('email'),
            created_at: rows.fieldByName('created_at')
        });
        rows.next();
    }
    
    rows.close();
    return users;
}

// ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ข…๋ฃŒ (์ค‘์š”!)
function closeDatabase() {
    if (db) {
        db.close();
    }
}

๐Ÿ“ ํŒŒ์ผ ์‹œ์Šคํ…œ ํ™œ์šฉ

์ด๋ฏธ์ง€๋‚˜ ๋ฌธ์„œ ํŒŒ์ผ์„ ์ €์žฅํ•  ๋•Œ๋Š” ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•ด:

// ์•ฑ ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ
var appDataDir = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory);

// ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ €์žฅ
function saveImageToLocal(imageBlob, filename) {
    var imageFile = Ti.Filesystem.getFile(appDataDir, filename);
    
    if (imageFile.write(imageBlob)) {
        Ti.API.info('์ด๋ฏธ์ง€ ์ €์žฅ ์„ฑ๊ณต: ' + imageFile.nativePath);
        return imageFile.nativePath;
    } else {
        Ti.API.error('์ด๋ฏธ์ง€ ์ €์žฅ ์‹คํŒจ');
        return null;
    }
}

// ์ €์žฅ๋œ ์ด๋ฏธ์ง€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
function loadImageFromLocal(filename) {
    var imageFile = Ti.Filesystem.getFile(appDataDir, filename);
    
    if (imageFile.exists()) {
        return imageFile.read();
    } else {
        Ti.API.warn('ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: ' + filename);
        return null;
    }
}

// ์บ์‹œ ์ •๋ฆฌ (์ €์žฅ ๊ณต๊ฐ„ ๊ด€๋ฆฌ)
function cleanupOldFiles() {
    var files = appDataDir.getDirectoryListing();
    var oneWeekAgo = new Date().getTime() - (7 * 24 * 60 * 60 * 1000);
    
    files.forEach(function(filename) {
        var file = Ti.Filesystem.getFile(appDataDir, filename);
        if (file.modificationTimestamp() < oneWeekAgo) {
            file.deleteFile();
            Ti.API.info('์˜ค๋ž˜๋œ ํŒŒ์ผ ์‚ญ์ œ: ' + filename);
        }
    });
}

๐ŸŽจ ๊ณ ๊ธ‰ UI ์ปดํฌ๋„ŒํŠธ์™€ ์• ๋‹ˆ๋ฉ”์ด์…˜

๊ธฐ๋ณธ์ ์ธ ๋ฒ„ํŠผ๊ณผ ๋ผ๋ฒจ๋งŒ์œผ๋กœ๋Š” ์•„์‰ฝ์ง€? ์ด์ œ ์ข€ ๋” ๋ฉ‹์ง„ UI๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! Titanium์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ณ ๊ธ‰ ์ปดํฌ๋„ŒํŠธ๋“ค๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ํ”„๋กœํŽ˜์…”๋„ํ•œ ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด! โœจ

๐ŸŽ  ์ปค์Šคํ…€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋งŒ๋“ค๊ธฐ

์‚ฌ์šฉ์ž์˜ ์‹œ์„ ์„ ์‚ฌ๋กœ์žก๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด์ž:

// ๋ถ€๋“œ๋Ÿฌ์šด ํŽ˜์ด๋“œ ์ธ ์• ๋‹ˆ๋ฉ”์ด์…˜
function fadeInView(view) {
    view.opacity = 0;
    view.animate({
        opacity: 1,
        duration: 500,
        curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
    });
}

// ํ†ตํ†ต ํŠ€๋Š” ๋ฒ„ํŠผ ์• ๋‹ˆ๋ฉ”์ด์…˜
function bounceButton(button) {
    var originalTransform = Ti.UI.create2DMatrix();
    var scaleTransform = Ti.UI.create2DMatrix().scale(1.2, 1.2);
    
    button.animate({
        transform: scaleTransform,
        duration: 100
    }, function() {
        button.animate({
            transform: originalTransform,
            duration: 100
        });
    });
}

// ์Šฌ๋ผ์ด๋“œ ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜
function slideToNextView(currentView, nextView) {
    var screenWidth = Ti.Platform.displayCaps.platformWidth;
    
    // ๋‹ค์Œ ๋ทฐ๋ฅผ ํ™”๋ฉด ์˜ค๋ฅธ์ชฝ์— ๋ฐฐ์น˜
    nextView.left = screenWidth;
    
    // ๋™์‹œ์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹คํ–‰
    currentView.animate({
        left: -screenWidth,
        duration: 300,
        curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
    });
    
    nextView.animate({
        left: 0,
        duration: 300,
        curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
    });
}

๐Ÿ“‹ ๊ณ ๊ธ‰ ๋ฆฌ์ŠคํŠธ ๋ทฐ ๊ตฌํ˜„

ListView๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์„ฑ๋Šฅ ์ข‹์€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž:

// ๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ ํ…œํ”Œ๋ฆฟ ์ •์˜
var itemTemplate = {
    childTemplates: [
        {
            type: 'Ti.UI.ImageView',
            bindId: 'avatar',
            properties: {
                width: '60dp',
                height: '60dp',
                left: '10dp',
                borderRadius: '30dp'
            }
        },
        {
            type: 'Ti.UI.Label',
            bindId: 'title',
            properties: {
                left: '80dp',
                top: '5dp',
                font: { fontSize: '18dp', fontWeight: 'bold' },
                color: '#333'
            }
        },
        {
            type: 'Ti.UI.Label',
            bindId: 'subtitle',
            properties: {
                left: '80dp',
                top: '30dp',
                font: { fontSize: '14dp' },
                color: '#666'
            }
        }
    ]
};

// ListView ์ƒ์„ฑ
var listView = Ti.UI.createListView({
    templates: { 'userTemplate': itemTemplate },
    defaultItemTemplate: 'userTemplate'
});

// ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ
function populateListView(users) {
    var listData = users.map(function(user) {
        return {
            avatar: { image: user.avatar_url || '/images/default_avatar.png' },
            title: { text: user.name },
            subtitle: { text: user.email },
            properties: {
                itemId: user.id,
                searchableText: user.name + ' ' + user.email
            }
        };
    });
    
    var section = Ti.UI.createListSection({
        items: listData
    });
    
    listView.sections = [section];
}

// ์•„์ดํ…œ ํด๋ฆญ ์ด๋ฒคํŠธ
listView.addEventListener('itemclick', function(e) {
    var userId = e.section.getItemAt(e.itemIndex).properties.itemId;
    openUserProfile(userId);
});

๐ŸŽฏ ์ œ์Šค์ฒ˜ ์ธ์‹

ํ„ฐ์น˜, ์Šค์™€์ดํ”„, ํ•€์น˜ ๋“ฑ์˜ ์ œ์Šค์ฒ˜๋ฅผ ์ธ์‹ํ•ด์„œ ๋” ์ง๊ด€์ ์ธ UI๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž:

// ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜
var gestureView = Ti.UI.createView({
    backgroundColor: '#f0f0f0',
    width: Ti.UI.FILL,
    height: '200dp'
});

gestureView.addEventListener('swipe', function(e) {
    switch (e.direction) {
        case 'left':
            slideToNextPage();
            break;
        case 'right':
            slideToPreviousPage();
            break;
        case 'up':
            showMoreOptions();
            break;
        case 'down':
            hideOptions();
            break;
    }
});

// ํ•€์น˜ ์คŒ ์ œ์Šค์ฒ˜ (์ด๋ฏธ์ง€ ํ™•๋Œ€/์ถ•์†Œ)
var imageView = Ti.UI.createImageView({
    image: '/images/photo.jpg',
    canScale: true,
    enableZoomControls: true
});

imageView.addEventListener('pinch', function(e) {
    Ti.API.info('ํ•€์น˜ ์Šค์ผ€์ผ: ' + e.scale);
    // ์ปค์Šคํ…€ ์คŒ ๋กœ์ง ๊ตฌํ˜„ ๊ฐ€๋Šฅ
});

// ๋กฑ ํ”„๋ ˆ์Šค ์ œ์Šค์ฒ˜
var longPressView = Ti.UI.createView();

longPressView.addEventListener('longpress', function(e) {
    showContextMenu(e.x, e.y);
});

๐Ÿ”ง ๋ชจ๋“ˆ๊ณผ ํ”Œ๋Ÿฌ๊ทธ์ธ ํ™œ์šฉํ•˜๊ธฐ

Titanium์˜ ๋˜ ๋‹ค๋ฅธ ๊ฐ•์ ์€ ํ’๋ถ€ํ•œ ๋ชจ๋“ˆ ์ƒํƒœ๊ณ„์•ผ! ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์œผ๋กœ ๋ถ€์กฑํ•˜๋‹ค๋ฉด ์„œ๋“œํŒŒํ‹ฐ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋“ˆ์„ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์–ด. ๐Ÿงฉ

๐Ÿ“ฆ ์ธ๊ธฐ ์žˆ๋Š” Titanium ๋ชจ๋“ˆ๋“ค

๐Ÿ“Š Ti.Chart

์•„๋ฆ„๋‹ค์šด ์ฐจํŠธ์™€ ๊ทธ๋ž˜ํ”„๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“ˆ์ด์•ผ. ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๊ฐ€ ํ•„์š”ํ•œ ์•ฑ์— ํ•„์ˆ˜!

๐Ÿ—บ๏ธ Ti.Map

Google Maps๋‚˜ Apple Maps๋ฅผ ์•ฑ์— ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์–ด. ์œ„์น˜ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ๊ฐœ๋ฐœ์— ์™„๋ฒฝ!

๐Ÿ“ท Ti.ImageFactory

์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ง•, ํฌ๋กญ, ํ•„ํ„ฐ ์ ์šฉ ๋“ฑ ๊ณ ๊ธ‰ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด!

๐Ÿ”” Ti.CloudPush

ํ‘ธ์‹œ ์•Œ๋ฆผ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ชจ๋“ˆ์ด์•ผ. ์‚ฌ์šฉ์ž ์ฐธ์—ฌ๋„ ํ–ฅ์ƒ์— ํ•„์ˆ˜!

๐Ÿ› ๏ธ ๋ชจ๋“ˆ ์„ค์น˜์™€ ์‚ฌ์šฉ๋ฒ•

๋ชจ๋“ˆ์„ ์„ค์น˜ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ •๋ง ๊ฐ„๋‹จํ•ด:

// 1. ๋ชจ๋“ˆ ์„ค์น˜ (ํ„ฐ๋ฏธ๋„์—์„œ)
ti module install ti.map

// 2. tiapp.xml์— ๋ชจ๋“ˆ ์ถ”๊ฐ€
<modules>
    <module>ti.map</module>
</modules>

// 3. ์ฝ”๋“œ์—์„œ ๋ชจ๋“ˆ ์‚ฌ์šฉ
var MapModule = require('ti.map');

var mapView = MapModule.createView({
    mapType: MapModule.NORMAL_TYPE,
    region: {
        latitude: 37.5665,
        longitude: 126.9780,
        latitudeDelta: 0.01,
        longitudeDelta: 0.01
    },
    animate: true,
    regionFit: true,
    userLocation: true
});

// ๋งˆ์ปค ์ถ”๊ฐ€
var annotation = MapModule.createAnnotation({
    latitude: 37.5665,
    longitude: 126.9780,
    title: "์„œ์šธ์‹œ์ฒญ",
    subtitle: "๋Œ€ํ•œ๋ฏผ๊ตญ ์„œ์šธํŠน๋ณ„์‹œ",
    pincolor: MapModule.ANNOTATION_RED
});

mapView.addAnnotation(annotation);

๐ŸŽจ ์ปค์Šคํ…€ ๋ชจ๋“ˆ ๋งŒ๋“ค๊ธฐ

๊ธฐ์กด ๋ชจ๋“ˆ๋กœ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๋Š” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์ง์ ‘ ๋ชจ๋“ˆ์„ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์–ด! ์ด๊ฑด ์ข€ ๊ณ ๊ธ‰ ๊ธฐ์ˆ ์ด์ง€๋งŒ, ์•Œ์•„๋‘๋ฉด ์ •๋ง ์œ ์šฉํ•ด:

// iOS ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋“ˆ ์˜ˆ์ œ (Objective-C)
#import "TiModule.h"

@interface MyCustomModule : TiModule
@end

@implementation MyCustomModule

- (NSString*)moduleId {
    return @"com.example.mycustommodule";
}

- (void)doSomethingSpecial:(id)args {
    // ๋„ค์ดํ‹ฐ๋ธŒ iOS ์ฝ”๋“œ ์‹คํ–‰
    NSString *result = @"๋„ค์ดํ‹ฐ๋ธŒ์—์„œ ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ";
    
    // JavaScript๋กœ ๊ฒฐ๊ณผ ์ „๋‹ฌ
    if ([args count] > 0) {
        KrollCallback *callback = [args objectAtIndex:0];
        [callback call:@[result] thisObject:nil];
    }
}

@end

๋ฌผ๋ก  ์ด๋Ÿฐ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์€ ์ฒ˜์Œ์—๋Š” ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๊ฒฝํ—˜ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์กฐ์–ธ์„ ๊ตฌํ•˜๋ฉด ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿค

๐Ÿงช ํ…Œ์ŠคํŒ…๊ณผ ๋””๋ฒ„๊น… ์ „๋žต

์ข‹์€ ์•ฑ์„ ๋งŒ๋“ค๋ ค๋ฉด ์ฒ ์ €ํ•œ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์ˆ˜์•ผ! ๋ฒ„๊ทธ ์—†๋Š” ์™„๋ฒฝํ•œ ์•ฑ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŒ… ์ „๋žต์„ ์•Œ์•„๋ณด์ž. ๐Ÿ”

๐Ÿ› ๋””๋ฒ„๊น… ๋„๊ตฌ ํ™œ์šฉ

Titanium์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๋””๋ฒ„๊น… ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ด:

// ์ฝ˜์†” ๋กœ๊น… ๋ ˆ๋ฒจ ์„ค์ •
Ti.API.setLogLevel(Ti.API.LOG_LEVEL_DEBUG);

// ์ƒ์„ธํ•œ ๋””๋ฒ„๊ทธ ์ •๋ณด
function debugUserAction(action, data) {
    Ti.API.debug(`[USER_ACTION] ${action}: ${JSON.stringify(data)}`);
    
    // ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ๋งŒ ์ƒ์„ธ ๋กœ๊ทธ ์ถœ๋ ฅ
    if (Ti.App.deployType === 'development') {
        console.log('์Šคํƒ ํŠธ๋ ˆ์ด์Šค:', new Error().stack);
    }
}

// ์„ฑ๋Šฅ ์ธก์ •
function measurePerformance(functionName, func) {
    var startTime = new Date().getTime();
    
    try {
        var result = func();
        var endTime = new Date().getTime();
        Ti.API.info(`${functionName} ์‹คํ–‰ ์‹œ๊ฐ„: ${endTime - startTime}ms`);
        return result;
    } catch (error) {
        Ti.API.error(`${functionName} ์‹คํ–‰ ์ค‘ ์—๋Ÿฌ: ${error.message}`);
        throw error;
    }
}

๐Ÿงช ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ

์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด:

// ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ
var TestFramework = {
    tests: [],
    
    addTest: function(name, testFunction) {
        this.tests.push({
            name: name,
            test: testFunction
        });
    },
    
    runTests: function() {
        var passed = 0;
        var failed = 0;
        
        this.tests.forEach(function(test) {
            try {
                test.test();
                Ti.API.info(`โœ… ${test.name} - ํ†ต๊ณผ`);
                passed++;
            } catch (error) {
                Ti.API.error(`โŒ ${test.name} - ์‹คํŒจ: ${error.message}`);
                failed++;
            }
        });
        
        Ti.API.info(`ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ: ${passed}๊ฐœ ํ†ต๊ณผ, ${failed}๊ฐœ ์‹คํŒจ`);
    },
    
    assert: function(condition, message) {
        if (!condition) {
            throw new Error(message || '์กฐ๊ฑด์ด false์ž…๋‹ˆ๋‹ค');
        }
    },
    
    assertEqual: function(actual, expected, message) {
        if (actual !== expected) {
            throw new Error(message || `์˜ˆ์ƒ๊ฐ’: ${expected}, ์‹ค์ œ๊ฐ’: ${actual}`);
        }
    }
};

// ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ ์˜ˆ์ œ
TestFramework.addTest('์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ', function() {
    var user = { name: 'John', email: 'john@example.com' };
    
    TestFramework.assert(user.name.length > 0, '์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ๋น„์–ด์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค');
    TestFramework.assert(user.email.includes('@'), '์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค');
});

TestFramework.addTest('๊ณ„์‚ฐ ํ•จ์ˆ˜ ํ…Œ์ŠคํŠธ', function() {
    function add(a, b) {
        return a + b;
    }
    
    TestFramework.assertEqual(add(2, 3), 5, '2 + 3์€ 5์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค');
    TestFramework.assertEqual(add(-1, 1), 0, '-1 + 1์€ 0์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค');
});

// ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ๋งŒ ํ…Œ์ŠคํŠธ ์‹คํ–‰
if (Ti.App.deployType === 'development') {
    TestFramework.runTests();
}

๐Ÿ“ฑ ์‹ค์ œ ๋””๋ฐ”์ด์Šค ํ…Œ์ŠคํŠธ

์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋‚˜ ์—๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ์˜ ํ…Œ์ŠคํŠธ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ์‹ค์ œ ๋””๋ฐ”์ด์Šค์—์„œ์˜ ํ…Œ์ŠคํŠธ๊ฐ€ ๋” ์ค‘์š”ํ•ด! ์„ฑ๋Šฅ, ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, ๋ฐฐํ„ฐ๋ฆฌ ์†Œ๋ชจ ๋“ฑ์„ ์ •ํ™•ํžˆ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ .

๐Ÿ“ฑ ์‹ค์ œ ๋””๋ฐ”์ด์Šค ํ…Œ์ŠคํŠธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ โ€ข ๋‹ค์–‘ํ•œ ํ™”๋ฉด ํฌ๊ธฐ์—์„œ ํ…Œ์ŠคํŠธ
โ€ข ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ ๋ณ€ํ™” ํ…Œ์ŠคํŠธ
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์ƒํ™ฉ ํ…Œ์ŠคํŠธ
โ€ข ๋ฐฐํ„ฐ๋ฆฌ ์ ˆ์•ฝ ๋ชจ๋“œ์—์„œ ํ…Œ์ŠคํŠธ
โ€ข ๋ฐฑ๊ทธ๋ผ์šด๋“œ/ํฌ๊ทธ๋ผ์šด๋“œ ์ „ํ™˜ ํ…Œ์ŠคํŠธ

๐Ÿš€ ์•ฑ ๋ฐฐํฌ์™€ ์Šคํ† ์–ด ๋“ฑ๋ก

๋“œ๋””์–ด ์•ฑ์ด ์™„์„ฑ๋˜์—ˆ๋‹ค๋ฉด ์ด์ œ ์„ธ์ƒ์— ๊ณต๊ฐœํ•  ์ฐจ๋ก€์•ผ! App Store์™€ Google Play Store์— ์•ฑ์„ ๋“ฑ๋กํ•˜๋Š” ๊ณผ์ •์„ ์•Œ์•„๋ณด์ž. ์ด ๊ณผ์ •์ด ์ข€ ๋ณต์žกํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฐจ๊ทผ์ฐจ๊ทผ ๋”ฐ๋ผํ•˜๋ฉด ๋ผ! ๐ŸŒŸ

๐Ÿ“ฆ ๋ฐฐํฌ์šฉ ๋นŒ๋“œ ๋งŒ๋“ค๊ธฐ

๊ฐœ๋ฐœ์šฉ ๋นŒ๋“œ์™€ ๋ฐฐํฌ์šฉ ๋นŒ๋“œ๋Š” ๋‹ฌ๋ผ. ๋ฐฐํฌ์šฉ ๋นŒ๋“œ๋Š” ์ตœ์ ํ™”๋˜๊ณ  ์„œ๋ช…๋œ ๋ฒ„์ „์ด์•ผ:

# iOS ๋ฐฐํฌ์šฉ ๋นŒ๋“œ
ti build -p ios -T dist-appstore

# Android ๋ฐฐํฌ์šฉ ๋นŒ๋“œ
ti build -p android -T dist-playstore

๐ŸŽ App Store ๋“ฑ๋ก ๊ณผ์ •

1

Apple Developer ๊ณ„์ • ์ค€๋น„

์—ฐ๊ฐ„ $99์˜ ๊ฐœ๋ฐœ์ž ๋“ฑ๋ก๋น„๋ฅผ ์ง€๋ถˆํ•˜๊ณ  Apple Developer Program์— ๊ฐ€์ž…ํ•ด์•ผ ํ•ด.

2

์ธ์ฆ์„œ์™€ ํ”„๋กœ๋น„์ €๋‹ ํ”„๋กœํŒŒ์ผ ์ƒ์„ฑ

Xcode๋‚˜ Apple Developer ํฌํ„ธ์—์„œ ๋ฐฐํฌ์šฉ ์ธ์ฆ์„œ๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ด.

3

App Store Connect์—์„œ ์•ฑ ์ •๋ณด ์ž…๋ ฅ

์•ฑ ์ด๋ฆ„, ์„ค๋ช…, ์Šคํฌ๋ฆฐ์ƒท, ํ‚ค์›Œ๋“œ ๋“ฑ์„ ์ž…๋ ฅํ•ด.

4

์•ฑ ๋ฐ”์ด๋„ˆ๋ฆฌ ์—…๋กœ๋“œ

Xcode๋‚˜ Application Loader๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋นŒ๋“œ๋œ .ipa ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด.

๐Ÿค– Google Play Store ๋“ฑ๋ก ๊ณผ์ •

1

Google Play Console ๊ณ„์ • ์ƒ์„ฑ

์ผํšŒ์„ฑ $25 ๋“ฑ๋ก๋น„๋ฅผ ์ง€๋ถˆํ•˜๊ณ  ๊ฐœ๋ฐœ์ž ๊ณ„์ •์„ ๋งŒ๋“ค์–ด.

2

์•ฑ ์„œ๋ช… ํ‚ค ์ƒ์„ฑ

Android ์•ฑ์€ ๋ฐ˜๋“œ์‹œ ์„œ๋ช…๋˜์–ด์•ผ ํ•ด. keytool์„ ์‚ฌ์šฉํ•ด์„œ ํ‚ค์Šคํ† ์–ด๋ฅผ ๋งŒ๋“ค์–ด.

3

์•ฑ ์ •๋ณด ์ž…๋ ฅ ๋ฐ APK ์—…๋กœ๋“œ

์•ฑ ์„ค๋ช…, ์Šคํฌ๋ฆฐ์ƒท, ๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ๋ฐฉ์นจ ๋“ฑ์„ ์ž…๋ ฅํ•˜๊ณ  APK ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด.

4

๊ฒ€ํ†  ๋ฐ ์ถœ์‹œ

Google์˜ ๊ฒ€ํ†  ๊ณผ์ •์„ ๊ฑฐ์ณ ์Šน์ธ๋˜๋ฉด Play Store์— ์•ฑ์ด ๊ณต๊ฐœ๋ผ!

๐Ÿ“ˆ ์•ฑ ์Šคํ† ์–ด ์ตœ์ ํ™” (ASO)

์•ฑ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ์‚ฌ๋žŒ๋“ค์ด ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด:

๐ŸŽฏ ASO ํ•ต์‹ฌ ์š”์†Œ๋“ค โ€ข ๋งค๋ ฅ์ ์ธ ์•ฑ ์ด๋ฆ„๊ณผ ๋ถ€์ œ๋ชฉ
โ€ข ํ‚ค์›Œ๋“œ ์ตœ์ ํ™”๋œ ์•ฑ ์„ค๋ช…
โ€ข ๊ณ ํ’ˆ์งˆ ์Šคํฌ๋ฆฐ์ƒท๊ณผ ์•ฑ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์˜์ƒ
โ€ข ๊ธ์ •์ ์ธ ์‚ฌ์šฉ์ž ๋ฆฌ๋ทฐ์™€ ํ‰์ 
โ€ข ์ •๊ธฐ์ ์ธ ์—…๋ฐ์ดํŠธ์™€ ๋ฒ„๊ทธ ์ˆ˜์ •
// ์•ฑ ๋‚ด์—์„œ ๋ฆฌ๋ทฐ ์š”์ฒญํ•˜๊ธฐ
function requestAppReview() {
    var launchCount = Ti.App.Properties.getInt('app_launch_count', 0);
    var hasRequestedReview = Ti.App.Properties.getBool('has_requested_review', false);
    
    // 10๋ฒˆ ์ด์ƒ ์‚ฌ์šฉํ–ˆ๊ณ  ์•„์ง ๋ฆฌ๋ทฐ ์š”์ฒญ์„ ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด
    if (launchCount >= 10 && !hasRequestedReview) {
        var dialog = Ti.UI.createAlertDialog({
            title: '์•ฑ์ด ๋งˆ์Œ์— ๋“œ์‹œ๋‚˜์š”? ๐Ÿ˜Š',
            message: '์•ฑ ์Šคํ† ์–ด์—์„œ ํ‰์ ์„ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค!',
            buttonNames: ['๋‚˜์ค‘์—', 'ํ‰์  ๋‚จ๊ธฐ๊ธฐ']
        });
        
        dialog.addEventListener('click', function(e) {
            if (e.index === 1) {
                // ์•ฑ ์Šคํ† ์–ด๋กœ ์ด๋™
                if (Ti.Platform.osname === 'iphone') {
                    Ti.Platform.openURL('https://apps.apple.com/app/id๋‹น์‹ ์˜์•ฑID');
                } else {
                    Ti.Platform.openURL('market://details?id=๋‹น์‹ ์˜ํŒจํ‚ค์ง€๋ช…');
                }
            }
            Ti.App.Properties.setBool('has_requested_review', true);
        });
        
        dialog.show();
    }
}

๐Ÿ”ฎ Titanium์˜ ๋ฏธ๋ž˜์™€ ๋Œ€์•ˆ ๊ธฐ์ˆ ๋“ค

๊ธฐ์ˆ ์˜ ์„ธ๊ณ„๋Š” ์ •๋ง ๋น ๋ฅด๊ฒŒ ๋ณ€ํ•ด. Titanium๋„ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๋‹ค๋ฅธ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ธฐ์ˆ ๋“ค๊ณผ์˜ ๊ฒฝ์Ÿ๋„ ์น˜์—ดํ•ด. ํ˜„์žฌ ์ƒํ™ฉ๊ณผ ๋ฏธ๋ž˜ ์ „๋ง์„ ๊ฐ๊ด€์ ์œผ๋กœ ์‚ดํŽด๋ณด์ž! ๐Ÿ”ญ

๐Ÿ“Š ํ˜„์žฌ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ธฐ์ˆ  ๋น„๊ต

ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ธฐ์ˆ  ๋น„๊ต Titanium JavaScript ๋„ค์ดํ‹ฐ๋ธŒ ์ปดํŒŒ์ผ ์„ฑ์ˆ™ํ•œ ์ƒํƒœ๊ณ„ React Native JavaScript/JSX ๋ธŒ๋ฆฌ์ง€ ๋ฐฉ์‹ Facebook ์ง€์› Flutter Dart ์ž์ฒด ๋ Œ๋”๋ง Google ์ง€์› Xamarin C# ๋„ค์ดํ‹ฐ๋ธŒ ์„ฑ๋Šฅ Microsoft ์ง€์› ์žฅ์  โ€ข ์ง„์งœ ๋„ค์ดํ‹ฐ๋ธŒ ์„ฑ๋Šฅ โ€ข ์˜ค๋žœ ์—ญ์‚ฌ์™€ ์•ˆ์ •์„ฑ โ€ข ํ’๋ถ€ํ•œ ๋ชจ๋“ˆ ์žฅ์  โ€ข ํฐ ์ปค๋ฎค๋‹ˆํ‹ฐ โ€ข React ๊ฐœ๋ฐœ์ž ์นœํ™”์  โ€ข ํ•ซ ๋ฆฌ๋กœ๋“œ ์žฅ์  โ€ข ๋น ๋ฅธ ์„ฑ๋Šฅ โ€ข ์ผ๊ด€๋œ UI โ€ข ํ•ซ ๋ฆฌ๋กœ๋“œ ์žฅ์  โ€ข .NET ์ƒํƒœ๊ณ„ โ€ข ๊ฐ•๋ ฅํ•œ IDE โ€ข ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์นœํ™”์  ๋‹จ์  โ€ข ์ƒ๋Œ€์ ์œผ๋กœ ์ž‘์€ ์ปค๋ฎค๋‹ˆํ‹ฐ โ€ข ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ง€์› ์ง€์—ฐ ๋‹จ์  โ€ข ๋ธŒ๋ฆฌ์ง€ ์˜ค๋ฒ„ํ—ค๋“œ โ€ข ํ”Œ๋žซํผ๋ณ„ ์ฐจ์ด ๋‹จ์  โ€ข ์ƒˆ๋กœ์šด ์–ธ์–ด ํ•™์Šต โ€ข ์•ฑ ํฌ๊ธฐ๊ฐ€ ํผ ๋‹จ์  โ€ข ๋ผ์ด์„ ์Šค ๋น„์šฉ โ€ข Windows ์ค‘์‹ฌ

๐ŸŽฏ ์–ธ์ œ Titanium์„ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ?

๊ฐ ๊ธฐ์ˆ ๋งˆ๋‹ค ์žฅ๋‹จ์ ์ด ์žˆ์œผ๋‹ˆ๊นŒ, ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ์— ๋งž๋Š” ์„ ํƒ์ด ์ค‘์š”ํ•ด:

โœ… Titanium์ด ์ข‹์€ ๊ฒฝ์šฐ

โ€ข JavaScript ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ํŒ€
โ€ข ๋„ค์ดํ‹ฐ๋ธŒ ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ์•ฑ
โ€ข ๊ธฐ์กด ์›น ๊ฐœ๋ฐœ ๋ฆฌ์†Œ์Šค ํ™œ์šฉ
โ€ข ์•ˆ์ •์„ฑ๊ณผ ์„ฑ์ˆ™๋„๊ฐ€ ์ค‘์š”ํ•œ ํ”„๋กœ์ ํŠธ
โ€ข ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์žˆ๋Š” ์•ฑ

๐Ÿค” ๋‹ค๋ฅธ ๊ธฐ์ˆ ์„ ๊ณ ๋ คํ•ด๋ณผ ๊ฒฝ์šฐ

โ€ข ์ตœ์‹  ํŠธ๋ Œ๋“œ๋ฅผ ๋”ฐ๋ผ๊ฐ€๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ
โ€ข ํฐ ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
โ€ข UI/UX๊ฐ€ ๋งค์šฐ ์ค‘์š”ํ•œ ์•ฑ
โ€ข ๋น ๋ฅธ ํ”„๋กœํ† ํƒ€์ดํ•‘์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
โ€ข ์›น ๊ธฐ์ˆ ๊ณผ์˜ ํ˜ธํ™˜์„ฑ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ

๐Ÿ”ฎ Titanium์˜ ๋ฏธ๋ž˜

Titanium์€ ๊ณ„์†ํ•ด์„œ ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์–ด. ์ตœ๊ทผ ์—…๋ฐ์ดํŠธ๋“ค์„ ๋ณด๋ฉด:

๐Ÿ†• ์ตœ์‹  OS ์ง€์›

iOS 17, Android 14 ๋“ฑ ์ตœ์‹  ์šด์˜์ฒด์ œ ์ง€์›์ด ๊ณ„์† ์ถ”๊ฐ€๋˜๊ณ  ์žˆ์–ด

โšก ์„ฑ๋Šฅ ๊ฐœ์„ 

๋นŒ๋“œ ์‹œ๊ฐ„ ๋‹จ์ถ•, ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ ํ–ฅ์ƒ ๋“ฑ ์ง€์†์ ์ธ ์ตœ์ ํ™”

๐Ÿ› ๏ธ ๊ฐœ๋ฐœ ๋„๊ตฌ ํ–ฅ์ƒ

๋” ๋‚˜์€ ๋””๋ฒ„๊น… ๋„๊ตฌ, IDE ์ง€์› ๊ฐœ์„ 

๐ŸŒ ๋ชจ๋˜ ์›น ๊ธฐ์ˆ  ํ†ตํ•ฉ

ES6+, TypeScript ์ง€์› ๋“ฑ ๋ชจ๋˜ JavaScript ๊ธฐ๋Šฅ ์ถ”๊ฐ€

๐Ÿ’ก ์‹ค๋ฌด์—์„œ ๋ฐฐ์šด ๊ฟ€ํŒ๋“ค

์ด๋ก ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ ์‹ค์ œ ๊ฐœ๋ฐœ์—์„œ ๋ถ€๋”ชํžˆ๋Š” ๋ฌธ์ œ๋“ค๊ณผ ํ•ด๊ฒฐ์ฑ…์„ ์•„๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ•ด! ๋‚ด๊ฐ€ ์‹ค๋ฌด์—์„œ ๊ฒฝํ—˜ํ•œ ๊ฟ€ํŒ๋“ค์„ ๊ณต์œ ํ•ด๋ณผ๊ฒŒ. ๐Ÿฏ

๐Ÿ› ์ž์ฃผ ๋งŒ๋‚˜๋Š” ๋ฌธ์ œ๋“ค๊ณผ ํ•ด๊ฒฐ์ฑ…

๐Ÿšจ ๋ฌธ์ œ 1: ์•ฑ์ด ๊ฐ‘์ž๊ธฐ ํฌ๋ž˜์‹œ๋ผ์š”! ํ•ด๊ฒฐ์ฑ…: ๋Œ€๋ถ€๋ถ„ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์ด๋‚˜ null ์ฐธ์กฐ ์—๋Ÿฌ์•ผ. try-catch ๋ธ”๋ก์„ ์ ๊ทน ํ™œ์šฉํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ฒดํฌํ•ด!
// ์•ˆ์ „ํ•œ ๊ฐ์ฒด ์ ‘๊ทผ ํŒจํ„ด
function safeAccess(obj, path, defaultValue) {
    try {
        var keys = path.split('.');
        var current = obj;
        
        for (var i = 0; i < keys.length; i++) {
            if (current === null || current === undefined) {
                return defaultValue;
            }
            current = current[keys[i]];
        }
        
        return current !== undefined ? current : defaultValue;
    } catch (error) {
        Ti.API.warn('์•ˆ์ „ํ•œ ์ ‘๊ทผ ์‹คํŒจ: ' + error.message);
        return defaultValue;
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
var userName = safeAccess(userObject, 'profile.personal.name', '์ต๋ช… ์‚ฌ์šฉ์ž');
๐Ÿšจ ๋ฌธ์ œ 2: ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ๋„ˆ๋ฌด ๋А๋ ค์š”! ํ•ด๊ฒฐ์ฑ…: ์š”์ฒญ์„ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ์บ์‹ฑ์„ ์ ๊ทน ํ™œ์šฉํ•ด! ๋˜ํ•œ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ์š”์ฒญํ•˜์ง€ ๋ง๊ณ , ํŽ˜์ด์ง•์„ ๊ตฌํ˜„ํ•ด.
// ๋ณ‘๋ ฌ API ์š”์ฒญ ์ฒ˜๋ฆฌ
function loadDashboardData() {
    var promises = [];
    
    // ์—ฌ๋Ÿฌ API๋ฅผ ๋™์‹œ์— ํ˜ธ์ถœ
    promises.push(fetchUserProfile());
    promises.push(fetchRecentPosts());
    promises.push(fetchNotifications());
    
    Promise.all(promises).then(function(results) {
        var userProfile = results[0];
        var recentPosts = results[1];
        var notifications = results[2];
        
        updateDashboardUI(userProfile, recentPosts, notifications);
    }).catch(function(error) {
        Ti.API.error('๋Œ€์‹œ๋ณด๋“œ ๋กœ๋”ฉ ์‹คํŒจ: ' + error.message);
        showErrorMessage('๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
    });
}

// ๊ฐ„๋‹จํ•œ ์บ์‹ฑ ์‹œ์Šคํ…œ
var cache = {};
var CACHE_DURATION = 5 * 60 * 1000; // 5๋ถ„

function getCachedData(key, fetchFunction) {
    var now = new Date().getTime();
    var cached = cache[key];
    
    if (cached && (now - cached.timestamp) < CACHE_DURATION) {
        Ti.API.info('์บ์‹œ์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜: ' + key);
        return Promise.resolve(cached.data);
    }
    
    return fetchFunction().then(function(data) {
        cache[key] = {
            data: data,
            timestamp: now
        };
        return data;
    });
}

๐ŸŽฏ ์ฝ”๋“œ ๊ตฌ์กฐํ™” ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ์ฝ”๋“œ ๊ตฌ์กฐ๊ฐ€ ์ค‘์š”ํ•ด์ ธ. ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๋“ค์ด์•ผ:

// ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋“ค์„ ๋ชจ๋“ˆํ™”
// lib/utils.js
exports.formatDate = function(date) {
    return date.toLocaleDateString('ko-KR');
};

exports.validateEmail = function(email) {
    var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
};

exports.showLoading = function(message) {
    var indicator = Ti.UI.createActivityIndicator({
        message: message || '๋กœ๋”ฉ ์ค‘...',
        style: Ti.UI.ActivityIndicatorStyle.DARK
    });
    indicator.show();
    return indicator;
};

// ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ
var Utils = require('/lib/utils');

function handleFormSubmit() {
    var email = $.emailField.value;
    
    if (!Utils.validateEmail(email)) {
        alert('์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
        return;
    }
    
    var loading = Utils.showLoading('๋ฐ์ดํ„ฐ ์ „์†ก ์ค‘...');
    
    submitForm(email).then(function() {
        loading.hide();
        showSuccessMessage('์„ฑ๊ณต์ ์œผ๋กœ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค!');
    }).catch(function(error) {
        loading.hide();
        showErrorMessage('์ „์†ก์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: ' + error.message);
    });
}

๐Ÿ”’ ๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญ

๋ชจ๋ฐ”์ผ ์•ฑ ๋ณด์•ˆ์€ ์ •๋ง ์ค‘์š”ํ•ด. ์‚ฌ์šฉ์ž์˜ ๊ฐœ์ธ์ •๋ณด๋ฅผ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ๋ณด์•ˆ ์กฐ์น˜๋“ค์„ ์•Œ์•„๋ณด์ž:

// API ํ‚ค๋‚˜ ๋ฏผ๊ฐํ•œ ์ •๋ณด ๋ณดํ˜ธ
var Config = {
    // โŒ ์ ˆ๋Œ€ ํ•˜์ง€ ๋ง ๊ฒƒ: ์†Œ์Šค์ฝ”๋“œ์— ์ง์ ‘ ํ•˜๋“œ์ฝ”๋”ฉ
    // API_KEY: 'sk-1234567890abcdef'
    
    // โœ… ํ™˜๊ฒฝ๋ณ€์ˆ˜๋‚˜ ์„ค์ • ํŒŒ์ผ ์‚ฌ์šฉ
    getApiKey: function() {
        return Ti.App.Properties.getString('api_key', '');
    },
    
    // ์„œ๋ฒ„์—์„œ ๋™์ ์œผ๋กœ ๋ฐ›์•„์˜ค๊ธฐ
    fetchApiConfig: function() {
        return new Promise(function(resolve, reject) {
            var xhr = Ti.Network.createHTTPClient({
                onload: function() {
                    try {
                        var config = JSON.parse(this.responseText);
                        resolve(config);
                    } catch (error) {
                        reject(error);
                    }
                },
                onerror: reject
            });
            xhr.open('GET', 'https://api.example.com/config');
            xhr.send();
        });
    }
};

// ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™” (๊ฐ„๋‹จํ•œ ์˜ˆ์ œ)
function encryptSensitiveData(data) {
    // ์‹ค์ œ๋กœ๋Š” ๋” ๊ฐ•๋ ฅํ•œ ์•”ํ˜ธํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ๊ถŒ์žฅ
    var encrypted = Ti.Utils.base64encode(Ti.Utils.sha256(data + 'secret_salt'));
    return encrypted;
}

// ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฒ€์ฆ
function sanitizeInput(input) {
    if (typeof input !== 'string') {
        return '';
    }
    
    // XSS ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ด์Šค์ผ€์ดํ”„
    return input
        .replace(/&/g, '&')
        .replace(/</g, '<')
        .replace(/>/g, '>')
        .replace(/"/g, '"')
        .replace(/'/g, ''');
}

๐ŸŽ“ ํ•™์Šต ๋ฆฌ์†Œ์Šค์™€ ์ปค๋ฎค๋‹ˆํ‹ฐ

Titanium์„ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ง€์†์ ์ธ ํ•™์Šต์ด ํ•„์š”ํ•ด! ์–ด๋””์„œ ๊ณต๋ถ€ํ•˜๊ณ  ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด์ž. ๐Ÿ“š

๐Ÿ“– ๊ณต์‹ ๋ฌธ์„œ์™€ ๊ฐ€์ด๋“œ

๐Ÿ“˜ Titanium SDK ๋ฌธ์„œ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” ๊ณต์‹ ๋ฌธ์„œ์•ผ. API ๋ ˆํผ๋Ÿฐ์Šค์™€ ๊ฐ€์ด๋“œ๊ฐ€ ์ž˜ ์ •๋ฆฌ๋˜์–ด ์žˆ์–ด!

๐ŸŽฏ Alloy ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐ€์ด๋“œ

MVC ํŒจํ„ด๊ณผ Alloy ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•œ ์ƒ์„ธํ•œ ์„ค๋ช…์„ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด.

๐Ÿ› ๏ธ ๊ฐœ๋ฐœ ๋„๊ตฌ ๋ฌธ์„œ

CLI ๋„๊ตฌ, Studio IDE ์‚ฌ์šฉ๋ฒ• ๋“ฑ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๊ตฌ์ถ•์— ํ•„์š”ํ•œ ์ •๋ณด๋“ค์ด์•ผ.

๐Ÿ“ฑ ํ”Œ๋žซํผ๋ณ„ ๊ฐ€์ด๋“œ

iOS์™€ Android ๊ฐ๊ฐ์˜ ํŠน์„ฑ๊ณผ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๊ฐ€์ด๋“œ๋ฅผ ์ œ๊ณตํ•ด.

๐ŸŒ ์ปค๋ฎค๋‹ˆํ‹ฐ์™€ ํฌ๋Ÿผ

ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๊ธฐ ์–ด๋ ค์šธ ๋•Œ๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋„์›€์„ ๋ฐ›์ž:

๐Ÿค Titanium ์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ์šฉ๋ฒ• โ€ข TiSlack: Titanium ๊ฐœ๋ฐœ์ž๋“ค์˜ ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…
โ€ข GitHub: ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ์™€ ์˜ˆ์ œ ์ฝ”๋“œ
โ€ข Stack Overflow: ๊ธฐ์ˆ ์  ์งˆ๋ฌธ๊ณผ ๋‹ต๋ณ€
โ€ข ์žฌ๋Šฅ๋„ท: ํ•œ๊ตญ์–ด๋กœ ๋ฉ˜ํ† ๋ง๊ณผ ๊ฐ•์˜ ์ˆ˜๊ฐ• ๊ฐ€๋Šฅ! ๐Ÿ‡ฐ๐Ÿ‡ท

๐Ÿš€ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด

์ด๋ก ๋งŒ ๊ณต๋ถ€ํ•˜์ง€ ๋ง๊ณ  ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ ์‹ค๋ ฅ์„ ๋Š˜๋ ค๋ณด์ž:

๐Ÿ“ ํ•  ์ผ ๊ด€๋ฆฌ ์•ฑ

๊ธฐ๋ณธ์ ์ธ CRUD ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์•Œ๋ฆผ, ์นดํ…Œ๊ณ ๋ฆฌ, ํ†ต๊ณ„ ๊ธฐ๋Šฅ๊นŒ์ง€ ์ถ”๊ฐ€ํ•ด๋ณด์ž!

๐Ÿ“ธ ์‚ฌ์ง„ ์ผ๊ธฐ ์•ฑ

์นด๋ฉ”๋ผ, ๊ฐค๋Ÿฌ๋ฆฌ, ์œ„์น˜ ์ •๋ณด, ๋กœ์ปฌ ์ €์žฅ์†Œ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ข…ํ•ฉ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿ’ฌ ๊ฐ„๋‹จํ•œ ์ฑ„ํŒ… ์•ฑ

์‹ค์‹œ๊ฐ„ ํ†ต์‹ , ํ‘ธ์‹œ ์•Œ๋ฆผ, ์‚ฌ์šฉ์ž ์ธ์ฆ ๋“ฑ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด!

๐Ÿ“Š ๊ฐœ์ธ ๊ฐ€๊ณ„๋ถ€ ์•ฑ

๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”, ์ฐจํŠธ, ๋ฆฌํฌํŠธ ์ƒ์„ฑ ๋“ฑ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ณต์žกํ•œ ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณด์ž!

๐Ÿ“ˆ ์ง€์†์ ์ธ ์„ฑ์žฅ์„ ์œ„ํ•œ ํŒ

// ๊ฐœ๋ฐœ ์ผ์ง€ ์ž‘์„ฑ ์Šต๊ด€
var DevLog = {
    log: function(category, message) {
        var timestamp = new Date().toISOString();
        var logEntry = `[${timestamp}] [${category}] ${message}`;
        
        Ti.API.info(logEntry);
        
        // ๋กœ์ปฌ์— ๊ฐœ๋ฐœ ๋กœ๊ทธ ์ €์žฅ (์„ ํƒ์‚ฌํ•ญ)
        this.saveToFile(logEntry);
    },
    
    saveToFile: function(entry) {
        var logFile = Ti.Filesystem.getFile(
            Ti.Filesystem.applicationDataDirectory, 
            'dev_log.txt'
        );
        
        var existingContent = logFile.exists() ? logFile.read().text : '';
        var newContent = existingContent + entry + '\n';
        
        logFile.write(newContent);
    },
    
    // ์ฃผ๊ฐ„ ํšŒ๊ณ 
    weeklyReview: function() {
        this.log('REVIEW', '์ด๋ฒˆ ์ฃผ์— ๋ฐฐ์šด ๊ฒƒ๋“ค:');
        this.log('REVIEW', '- ์ƒˆ๋กœ์šด API ์‚ฌ์šฉ๋ฒ•');
        this.log('REVIEW', '- ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ•');
        this.log('REVIEW', '- ๋””๋ฒ„๊น… ์Šคํ‚ฌ ํ–ฅ์ƒ');
    }
};

// ์‚ฌ์šฉ ์˜ˆ์ œ
DevLog.log('FEATURE', 'GPS ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์™„๋ฃŒ');
DevLog.log('BUG', '๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฌธ์ œ ํ•ด๊ฒฐ');
DevLog.log('PERFORMANCE', '๋ฆฌ์ŠคํŠธ ๋ Œ๋”๋ง ์†๋„ 50% ํ–ฅ์ƒ');

๐ŸŽ‰ ๋งˆ๋ฌด๋ฆฌ: Titanium์œผ๋กœ ๊ฟˆ์„ ํ˜„์‹ค๋กœ!

์™€! ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐Ÿƒโ€โ™‚๏ธ Titanium์˜ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๊นŒ์ง€, ์‹ค๋ฌด ํŒ๋ถ€ํ„ฐ ๋ฏธ๋ž˜ ์ „๋ง๊นŒ์ง€... ์ •๋ง ๋งŽ์€ ๋‚ด์šฉ์„ ๋‹ค๋ค˜์–ด!

Titanium์€ ๋ถ„๋ช…ํžˆ ๊ฐ•๋ ฅํ•œ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ๊ฐœ๋ฐœ ๋„๊ตฌ์•ผ. JavaScript ํ•˜๋‚˜๋กœ iOS์™€ Android ์•ฑ์„ ๋™์‹œ์— ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑด ์ •๋ง ๋งค๋ ฅ์ ์ด์ง€. ํŠนํžˆ ์›น ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์žˆ๋‹ค๋ฉด ๋”์šฑ ๋น ๋ฅด๊ฒŒ ๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœ์— ๋›ฐ์–ด๋“ค ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

๐ŸŽฏ ํ•ต์‹ฌ ํฌ์ธํŠธ ์ •๋ฆฌ

โšก ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ

ํ•œ ๋ฒˆ์˜ ๊ฐœ๋ฐœ๋กœ ๋‘ ํ”Œ๋žซํผ์„ ๋™์‹œ์— ์ง€์›ํ•˜๋‹ˆ๊นŒ ์‹œ๊ฐ„๊ณผ ๋น„์šฉ์„ ํฌ๊ฒŒ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿ† ๋„ค์ดํ‹ฐ๋ธŒ ์„ฑ๋Šฅ

ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•ฑ์ด ์•„๋‹ˆ๋ผ ์ง„์งœ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋กœ ์ปดํŒŒ์ผ๋˜๋‹ˆ๊นŒ ์„ฑ๋Šฅ ๊ฑฑ์ •์€ ์—†์–ด!

๐Ÿ› ๏ธ ํ’๋ถ€ํ•œ ๊ธฐ๋Šฅ

์นด๋ฉ”๋ผ, GPS, ํ‘ธ์‹œ ์•Œ๋ฆผ ๋“ฑ ์Šค๋งˆํŠธํฐ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ JavaScript๋กœ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

๐ŸŒฑ ์ง€์†์ ์ธ ๋ฐœ์ „

์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋กœ์„œ ๊ณ„์†ํ•ด์„œ ๋ฐœ์ „ํ•˜๊ณ  ์žˆ๊ณ , ์ปค๋ฎค๋‹ˆํ‹ฐ ์ง€์›๋„ ํ™œ๋ฐœํ•ด!

๐Ÿš€ ๋‹ค์Œ ๋‹จ๊ณ„๋Š”?

์ด์ œ ์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ฐฐ์› ์œผ๋‹ˆ ์‹ค์ œ๋กœ ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณด์ž! ์ž‘์€ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์  ๋ณต์žกํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ถ”๊ฐ€ํ•ด๋‚˜๊ฐ€๋ฉด ๋ผ. ๐ŸŽฏ

๐ŸŽ“ ํ•™์Šต ๋กœ๋“œ๋งต ์ถ”์ฒœ 1. ๊ฐ„๋‹จํ•œ "Hello World" ์•ฑ ๋งŒ๋“ค๊ธฐ
2. ๊ธฐ๋ณธ UI ์ปดํฌ๋„ŒํŠธ๋“ค ์ตํžˆ๊ธฐ
3. ๋ฐ์ดํ„ฐ ์ €์žฅ๊ณผ API ํ†ต์‹  ๊ตฌํ˜„
4. ๋„ค์ดํ‹ฐ๋ธŒ ๊ธฐ๋Šฅ๋“ค ํ™œ์šฉํ•ด๋ณด๊ธฐ
5. ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ํ…Œ์ŠคํŒ…
6. ์‹ค์ œ ์•ฑ ์Šคํ† ์–ด์— ๋ฐฐํฌํ•ด๋ณด๊ธฐ

๊ทธ๋ฆฌ๊ณ  ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ํ˜ผ์ž ๋™๋™๋Œ€์ง€ ๋ง๊ณ  ๋„์›€์„ ์š”์ฒญํ•ด! ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋Š” ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค๋กœ๋ถ€ํ„ฐ ์ง์ ‘ ๋ฉ˜ํ† ๋ง์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ๋•Œ๋กœ๋Š” ๋ช‡ ์‹œ๊ฐ„์˜ ๋ฉ˜ํ† ๋ง์ด ๋ฉฐ์น ๊ฐ„์˜ ์‚ฝ์งˆ์„ ์ค„์—ฌ์ค„ ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐Ÿ˜Š

๐Ÿ’ช ๋งˆ์ง€๋ง‰ ์‘์› ๋ฉ”์‹œ์ง€

๋ชจ๋ฐ”์ผ ์•ฑ ๊ฐœ๋ฐœ์€ ์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•œ ๋ถ„์•ผ์•ผ! ๋‚ด๊ฐ€ ๋งŒ๋“  ์•ฑ์„ ์ „ ์„ธ๊ณ„ ์‚ฌ๋žŒ๋“ค์ด ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ด. ์–ผ๋งˆ๋‚˜ ๋ฉ‹์ง„ ์ผ์ด์•ผ? ๐ŸŒ

Titanium์€ ๊ทธ ๊ฟˆ์„ ํ˜„์‹ค๋กœ ๋งŒ๋“ค์–ด์ค„ ์ˆ˜ ์žˆ๋Š” ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์•ผ. ๋ฌผ๋ก  ์ฒ˜์Œ์—๋Š” ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์ง€๋งŒ, ํฌ๊ธฐํ•˜์ง€ ๋ง๊ณ  ๊พธ์ค€ํžˆ ์—ฐ์Šตํ•˜๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ ๋ฉ‹์ง„ ์•ฑ์„ ๋งŒ๋“ค๊ณ  ์žˆ๋Š” ์ž์‹ ์„ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋  ๊ฑฐ์•ผ! ๐Ÿ’ช

๊ทธ๋Ÿผ ์ด์ œ ์ปดํ“จํ„ฐ ์•ž์— ์•‰์•„์„œ ์ฒซ ๋ฒˆ์งธ Titanium ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณด์ž! ํ™”์ดํŒ…! ๐Ÿ”ฅ

๐ŸŽŠ Happy Coding! ๊ธฐ์–ตํ•ด, ๋ชจ๋“  ์ „๋ฌธ๊ฐ€๋„ ์ฒ˜์Œ์—๋Š” ์ดˆ๋ณด์ž์˜€์–ด. ์ค‘์š”ํ•œ ๊ฑด ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด๊ณ , ๊พธ์ค€ํžˆ ํ•˜๋Š” ๊ฒƒ์ด์•ผ. ๋„ˆ๋„ ๋ถ„๋ช…ํžˆ ๋ฉ‹์ง„ ์•ฑ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐ŸŒŸ
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.