π―μμ΄ν° μλνλ‘ μ€μκ° νμ¨πΉ 보기 - νμ¨ κ³μ°κΈ° μμ ― μ€μ κ°μ΄λ πΈ
π² Scriptableλ‘ νμ¨ κ³μ°κΈ° μμ ― μ€μ ! μ€ν¬λ¦½νΈ μΆκ°λΆν° ν νλ©΄ μ€μκ° νμΈκΉμ§ κ°λ¨ν λ¨κ³λ‘ μλ£νμΈμ. πΉ μ½κ³ λΉ λ₯Έ νμ¨ κ΄λ¦¬!
- 1οΈβ£ Scriptable μ± μ€μΉ π²
- 2οΈβ£ νμ¨ μ€ν¬λ¦½νΈ μΆκ°νκΈ° π
- 3οΈβ£ μμ ―μ νμ¨ κ³μ°κΈ° μΆκ°νκΈ° π
- 4οΈβ£ λ€ν¬ λͺ¨λ μ€μ λ° μ¬λΆν π
- 5οΈβ£ μ€μκ° νμ¨ μμ ― νμ© ν π
- β μμ£Ό 묻λ μ§λ¬Έ (Q&A)
- π₯ νμ¨ νμ, νμ¨ κ³μ° μμ ―μ μν μ€μ λ°©λ² μκ°
1οΈβ£ Scriptable μ± μ€μΉ π²
Scriptable μ±μ μμ΄ν° μμ ―μ μ¬μ©μνν μ μλ κ°λ ₯ν λꡬμ
λλ€.
π μ±μ€ν μ΄ μ μ: ‘Scriptable’μ κ²μν΄ λ¬΄λ£λ‘ λ€μ΄λ‘λν©λλ€.
π μ± μ€ν: μ€μΉ ν Scriptableμ μ΄κ³ νλ©΄ νλ¨μ ‘+’ λ²νΌμ λλ¬ μ μ€ν¬λ¦½νΈλ₯Ό μΆκ°ν μ€λΉλ₯Ό ν©λλ€.
2οΈβ£ νμ¨ μ€ν¬λ¦½νΈ μΆκ°νκΈ° π
νμ¨ μ 보λ₯Ό μλμΌλ‘ λΆλ¬μ νμνλ μ€ν¬λ¦½νΈλ₯Ό μ€μ νμΈμ.
πΉ μ€ν¬λ¦½νΈ 볡μ¬: μ 곡λ νμ¨ μ€ν¬λ¦½νΈλ₯Ό 볡μ¬ν©λλ€.
πΉ μ€ν¬λ¦½νΈ λΆμ¬λ£κΈ°: Scriptable μ±μμ ‘μ μ€ν¬λ¦½νΈ’λ₯Ό μ ννκ³ λ³΅μ¬ν μ½λλ₯Ό λΆμ¬λ£μ΅λλ€.
πΉ μ€ν¬λ¦½νΈ μ΄λ¦ μ€μ : μ΄λ¦μ ‘Exchange Rate’ λλ ‘νμ¨ κ³μ°κΈ°’λ‘ μ€μ ν΄ κ΅¬λΆνκΈ° μ½κ² ν©λλ€.
πΉ μλ£ ν μ μ₯: μ°μΈ‘ μλ¨μμ μλ£λ₯Ό λλ¬ μ μ₯ν©λλ€.
3οΈβ£ μμ ―μ νμ¨ κ³μ°κΈ° μΆκ°νκΈ° π
ν νλ©΄μμ λ°λ‘ νμΈν μ μλλ‘ νμ¨ κ³μ°κΈ°λ₯Ό μμ ―μΌλ‘ μ€μ νμΈμ.
πΉ ν νλ©΄ νΈμ§: μμ΄ν° ν νλ©΄μ κΈΈκ² λλ₯΄κ³ ‘+’ μμ΄μ½μ λλ¬ Scriptable μμ ―μ μΆκ°ν©λλ€.
πΉ μμ ― μ ν: Scriptable μμ ―μ μ ννκ³ , μμ±ν νμ¨ κ³μ°κΈ° μ€ν¬λ¦½νΈλ₯Ό μ§μ ν©λλ€.
πΉ μμ ― ν¬κΈ° μ€μ : νμμ λ°λΌ μμ ― ν¬κΈ°λ₯Ό μ‘°μ νκ³ μ€μκ° νμ¨ μ 보λ₯Ό νμΈν μ μλλ‘ ν©λλ€.
4οΈβ£ λ€ν¬ λͺ¨λ μ€μ λ° μ¬λΆν π
νμ¨ μμ ―μ λ€ν¬ λͺ¨λ λλ λΌμ΄νΈ λͺ¨λλ₯Ό μ μ©ν μ μμ΅λλ€.
πΉ λ€ν¬ λͺ¨λ μ€μ νμΈ: Scriptable λ΄ λ€ν¬ λͺ¨λ μ΅μ
μ΄ μλμ§ νμΈνμΈμ.
πΉ κΈ°κΈ° μ¬λΆν
: μ€μ μΆ©λ μ κΈ°κΈ°λ₯Ό μ¬λΆν
νμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ΅λλ€.
πΉ μΆκ° μ€μ : λ¨μΆμ΄ μ±μΌλ‘ μλ μ¬λΆν
μ€μΌμ€μ μ€μ ν΄ μ¬μ©μ νΈμλ₯Ό λμΌ μ μμ΅λλ€.
5οΈβ£ μ€μκ° νμ¨ μμ ― νμ© ν π
μ£Όμ ν΅ν μΆκ°: μμ ―μμ μμ£Ό νμΈνλ ν΅νλ₯Ό μΆκ°ν΄ κ°μΈ λ§μΆ€ν νμ¨ μ 보λ₯Ό μ€μ νμΈμ.
μ¬ν μ μ μ©: ν΄μΈμ¬ν μ€ μ€μκ° νμ¨μ νμΈνκ±°λ νμ μ νμ©νκΈ° μ’μ΅λλ€.
λ€λ₯Έ κΈ°κΈ° μ°λ: μμ΄ν°λΏ μλλΌ μμ΄ν¨λ, μ νμμΉμλ μ°λν΄ νΈλ¦¬νκ² νμ¨ μ 보λ₯Ό κ΄λ¦¬ν μ μμ΅λλ€.
β μμ£Ό 묻λ μ§λ¬Έ (Q&A)
Q1: Scriptable μ±μ 무λ£μΈκ°μ?
A: λ€, Scriptable μ±μ 무λ£λ‘ μ 곡λλ©° μΌλΆ κ³ κΈ κΈ°λ₯μ μν΄μλ μ λ£ κ²°μ κ° νμν μ μμ΅λλ€.
Q2: νμ¨ λ°μ΄ν°λ μΌλ§λ μμ£Ό μ
λ°μ΄νΈλλμ?
A: Scriptableμμ μ¬μ©νλ νμ¨ λ°μ΄ν°λ μ€μκ°μΌλ‘ κ°±μ λλ©°, μΈν°λ· μ°κ²° μ μ΅μ λ°μ΄ν°λ₯Ό λ°μμ΅λλ€.
Q3: λ€ν¬ λͺ¨λκ° μ μ©λμ§ μμΌλ©΄ μ΄λ»κ² νλμ?
A: κΈ°κΈ°λ₯Ό μ¬λΆν
νκ±°λ μμ ― λ°°κ²½ μ€μ μ μ‘°μ ν΄ λ³΄μΈμ. μΌλΆ κΈ°κΈ°μμλ μ¬λΆν
μΌλ‘ ν΄κ²°λ μ μμ΅λλ€.
Q4: Scriptable μΈ λ€λ₯Έ μ±μΌλ‘λ μ€μκ° νμ¨ νμΈμ΄ κ°λ₯νκ°μ?
A: λ€, μ¬λ¬ νμ¨ μ±μ΄ μμ§λ§ Scriptableμ μ§μ μ€ν¬λ¦½νΈλ₯Ό μ¬μ©ν΄ λ λ§μ λ§μΆ€νκ° κ°λ₯ν©λλ€.
Q5: ν΅νλ₯Ό μΆκ°νκ³ μΆμλ°, μ΄λ»κ² ν΄μΌ νλμ?
A: μ€ν¬λ¦½νΈ λ΄ CURRENCIES λͺ©λ‘μ μνλ ν΅ν μ½λλ₯Ό μΆκ°νλ©΄ λ©λλ€.
π₯ νμ¨ νμ, νμ¨ κ³μ° μμ ―μ μν μ€μ λ°©λ² μκ°
π‘ νμ¨ νμ μλ°μ€ν¬λ¦½νΈ (Exchange Rate)
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: brown; icon-glyph: magic;
// Configuration
const CONFIG = {
BASE_CURRENCY: 'KRW',
AMOUNT: 1000,
CURRENCIES: ['USD', 'EUR', 'JPY', 'CNY'],
FLAG_URLS: {
USD: "https://www.ecb.europa.eu/shared/img/flags/USD.gif",
EUR: "https://www.countryflags.com/wp-content/uploads/europe-flag-jpg-xl-300x200.jpg",
JPY: "https://www.ecb.europa.eu/shared/img/flags/JPY.gif",
CNY: "https://www.ecb.europa.eu/shared/img/flags/CNY.gif"
},
DECIMAL_PLACES: {
USD: 1,
EUR: 1,
JPY: 4,
CNY: 2
}
};
// Get formatted current date
function getCurrentDate() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// Main function
async function main() {
try {
const data = await fetchExchangeRates();
const images = await fetchFlags();
const widget = await createWidget(data, images);
if (config.runsInWidget) {
Script.setWidget(widget);
} else {
await widget.presentSmall();
}
} catch (error) {
console.error(`Error: ${error}`);
const errorWidget = createErrorWidget(error);
Script.setWidget(errorWidget);
}
Script.complete();
}
// Fetch exchange rates
async function fetchExchangeRates() {
const url = `https://api.frankfurter.app/latest?amount=${CONFIG.AMOUNT}&from=${CONFIG.BASE_CURRENCY}`;
const req = new Request(url);
const res = await req.loadJSON();
if (!res.rates) {
throw new Error("Invalid data from exchange rate API");
}
return {
amount: res.amount || CONFIG.AMOUNT,
date: getCurrentDate(),
rates: res.rates
};
}
// Fetch flag images
async function fetchFlags() {
const imgRequests = CONFIG.CURRENCIES.map(currency =>
new Request(CONFIG.FLAG_URLS[currency])
);
try {
return await Promise.all(imgRequests.map(req => req.loadImage()));
} catch (error) {
console.error('Failed to load some flag images:', error);
return Array(CONFIG.CURRENCIES.length).fill(null);
}
}
// Create the main widget
function createWidget(data, images) {
const widget = new ListWidget();
widget.setPadding(8, 8, 8, 8);
// Theme setup
const isDarkMode = Device.isUsingDarkAppearance();
const theme = {
background: isDarkMode ? new Color("1C1C1E") : new Color("FFFFFF"),
title: new Color("0EB4FC"),
text: isDarkMode ? new Color("FFFFFF") : new Color("000000"),
subtext: new Color("A0A0A0")
};
widget.backgroundColor = theme.background;
// Title stack
const titleStack = widget.addStack();
titleStack.layoutVertically();
const titleText = titleStack.addText(`νμ¨ (${data.date})`);
titleText.font = Font.boldSystemFont(14);
titleText.textColor = theme.title;
titleStack.addSpacer(10);
// Currency rows
const currencyStack = titleStack.addStack();
currencyStack.layoutVertically();
CONFIG.CURRENCIES.forEach((currency, index) => {
const amount = (data.amount / data.rates[currency]).toFixed(CONFIG.DECIMAL_PLACES[currency] || 2);
addCurrencyRow(
currencyStack,
amount,
currency,
images[index],
theme
);
});
return widget;
}
// Add a currency row to the widget
function addCurrencyRow(stack, amount, currency, flagImage, theme) {
const rowStack = stack.addStack();
rowStack.layoutHorizontally();
rowStack.centerAlignContent();
if (flagImage) {
const flag = rowStack.addImage(flagImage);
flag.imageSize = new Size(20, 20);
rowStack.addSpacer(4);
}
const amountText = rowStack.addText(`${amount} `);
amountText.font = Font.boldSystemFont(20);
amountText.textColor = theme.text;
const currencyText = rowStack.addText(currency);
currencyText.font = Font.boldSystemFont(20);
currencyText.textColor = theme.subtext;
stack.addSpacer(4);
return rowStack;
}
// Create an error widget
function createErrorWidget(error) {
const widget = new ListWidget();
widget.backgroundColor = new Color("FF0000", 0.3);
const errorText = widget.addText("Error loading exchange rates");
errorText.font = Font.boldSystemFont(12);
errorText.textColor = new Color("FF0000");
return widget;
}
// Run the main function
await main();
π‘ νμ¨ κ³μ° μλ°μ€ν¬λ¦½νΈ (Exchange Calc) : 100λ§μ λ¨μλ‘ μ€μ ν΄λμμΌλ©°, 56λ² νμ μμ νλ©΄ λ€λ₯Έ μλ¨μλ‘λ κ³μ°κ°λ₯ν©λλ€.
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: brown; icon-glyph: magic;
// Configuration
const CONFIG = {
BASE_CURRENCY: 'KRW',
AMOUNT: 1000,
CURRENCIES: ['USD', 'EUR', 'JPY', 'CNY'],
FLAG_URLS: {
USD: "https://www.ecb.europa.eu/shared/img/flags/USD.gif",
EUR: "https://www.countryflags.com/wp-content/uploads/europe-flag-jpg-xl-300x200.jpg",
JPY: "https://www.ecb.europa.eu/shared/img/flags/JPY.gif",
CNY: "https://www.ecb.europa.eu/shared/img/flags/CNY.gif"
},
DECIMAL_PLACES: {
USD: 1,
EUR: 1,
JPY: 4,
CNY: 2
}
};
// Get formatted current date
function getCurrentDate() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// Main function
async function main() {
try {
const data = await fetchExchangeRates();
const images = await fetchFlags();
const widget = await createWidget(data, images);
if (config.runsInWidget) {
Script.setWidget(widget);
} else {
await widget.presentSmall();
}
} catch (error) {
console.error(`Error: ${error}`);
const errorWidget = createErrorWidget(error);
Script.setWidget(errorWidget);
}
Script.complete();
}
// Fetch exchange rates
async function fetchExchangeRates() {
const url = `https://api.frankfurter.app/latest?amount=${CONFIG.AMOUNT}&from=${CONFIG.BASE_CURRENCY}`;
const req = new Request(url);
const res = await req.loadJSON();
if (!res.rates) {
throw new Error("Invalid data from exchange rate API");
}
return {
amount: res.amount || CONFIG.AMOUNT,
date: getCurrentDate(),
rates: res.rates
};
}
// Fetch flag images
async function fetchFlags() {
const imgRequests = CONFIG.CURRENCIES.map(currency =>
new Request(CONFIG.FLAG_URLS[currency])
);
try {
return await Promise.all(imgRequests.map(req => req.loadImage()));
} catch (error) {
console.error('Failed to load some flag images:', error);
return Array(CONFIG.CURRENCIES.length).fill(null);
}
}
// Create the main widget
function createWidget(data, images) {
const widget = new ListWidget();
widget.setPadding(8, 8, 8, 8);
// Theme setup
const isDarkMode = Device.isUsingDarkAppearance();
const theme = {
background: isDarkMode ? new Color("1C1C1E") : new Color("FFFFFF"),
title: new Color("0EB4FC"),
text: isDarkMode ? new Color("FFFFFF") : new Color("000000"),
subtext: new Color("A0A0A0")
};
widget.backgroundColor = theme.background;
// Title stack
const titleStack = widget.addStack();
titleStack.layoutVertically();
const titleText = titleStack.addText(`νμ¨ (${data.date})`);
titleText.font = Font.boldSystemFont(14);
titleText.textColor = theme.title;
titleStack.addSpacer(10);
// Currency rows
const currencyStack = titleStack.addStack();
currencyStack.layoutVertically();
CONFIG.CURRENCIES.forEach((currency, index) => {
const amount = (data.amount / data.rates[currency]).toFixed(CONFIG.DECIMAL_PLACES[currency] || 2);
addCurrencyRow(
currencyStack,
amount,
currency,
images[index],
theme
);
});
return widget;
}
// Add a currency row to the widget
function addCurrencyRow(stack, amount, currency, flagImage, theme) {
const rowStack = stack.addStack();
rowStack.layoutHorizontally();
rowStack.centerAlignContent();
if (flagImage) {
const flag = rowStack.addImage(flagImage);
flag.imageSize = new Size(20, 20);
rowStack.addSpacer(4);
}
const amountText = rowStack.addText(`${amount} `);
amountText.font = Font.boldSystemFont(20);
amountText.textColor = theme.text;
const currencyText = rowStack.addText(currency);
currencyText.font = Font.boldSystemFont(20);
currencyText.textColor = theme.subtext;
stack.addSpacer(4);
return rowStack;
}
// Create an error widget
function createErrorWidget(error) {
const widget = new ListWidget();
widget.backgroundColor = new Color("FF0000", 0.3);
const errorText = widget.addText("Error loading exchange rates");
errorText.font = Font.boldSystemFont(12);
errorText.textColor = new Color("FF0000");
return widget;
}
// Run the main function
await main();
μ΄μ μμ΄ν°μμ μ€μκ° νμ¨ μμ ―μ μ½κ² νμΈνκ³ νμ©ν΄ 보μΈμ! κΆκΈν μ μ μΈμ λ λκΈλ‘ λ¨κ²¨μ£ΌμΈμ. π