단위 변환기: 온도

Prev Next

To download the app, visit: [tulip](https://<span translate=).co/library/apps/unit-converter--temperature/">Library

이 글에서는 저희 라이브러리 개발자 중 한 명인 타마스가 라이브러리에 포함된 ‘단위 변환기: 온도(Unit Converter: Temperature)’ 빌딩 블록 앱의 개발 배경에 대해 설명합니다.

테이블 대신 객체 목록을 사용한 이유

안녕하세요, 튤립의 타마스입니다.

이 컴포넌트의 경우, 데이터 세트가 거의 변경되지 않고 테이블 기반의 관리가 필요하지 않기 때문에 기본 변수 값으로 저장된 객체 목록을 사용하기로 결정했습니다.

이 경우, 온도 변환 계수와 오프셋은 정적인 수학적 관계입니다. 런타임 편집, 집계 또는 관계형 쿼리가 필요하지 않습니다. 그 때문에 저는 의도적으로 테이블 사용을 피했습니다. 대신: * 변수 내에 변환 정의를 저장합니다. * 변수는 기본값으로 초기화됩니다. * 객체 목록은 구조화되고 가독성 높은 데이터 뷰를 제공합니다. * 테이블 집계 로직이 필요하지 않습니다.

이러한 접근 방식은 컴포넌트를 가볍고 독립적으로 유지해 줍니다.

왜 테이블을 사용하지 않는가?

테이블을 사용하면 다음과 같은 문제가 발생합니다:* 앱에 추가 테이블을 생성해야 함* 해당 테이블에 대한 쿼리를 생성해야 함* 해당 쿼리에 대한 집계 로직을 구성해야 함* 앱 내에서 해당 쿼리와 집계 로직을 추가하고 유지 관리해야 함

변경 빈도가 낮은 데이터 세트의 경우, 이러한 오버헤드는 불필요합니다.

변환 정의를 변수 내의 기본값으로 저장함으로써:* 컴포넌트가 완전히 이식 가능해집니다* 앱을 복사하면 데이터 구조도 함께 복사됩니다* 외부 의존성이 필요하지 않습니다

객체 목록은 관계형 로직 없이도 관련 속성(inputUnit-targetUnit, factor, offset)을 깔끔하고 구조화된 형태로 보여줍니다.

설계 원칙: 보호된 로직, 유연한 구성

이 컴포넌트는 하나의 핵심 원칙을 바탕으로 설계되었습니다:

핵심 로직은 절대로 수정되어서는 안 됩니다.

이를 지원하기 위해 내부 컴포넌트 변수를 도입하고, 변수 이름의 맨 앞에 밑줄(예: _temperatureInputUnit)을 붙였습니다. 이러한 밑줄 변수는 보호된 핵심 로직이 의존할 수 있는 안정적인 내부 인터페이스 역할을 합니다.

장단점: 내부 변수를 사용하려면 두 가지 간단한 구성 트리거가 필요합니다:* 01 – 사용자 구성: 입력 단계(INPUT STAGE)에서 외부 소스를 내부 변수로 매핑* 03 – 사용자 구성: 출력 단계(OUTPUT STAGE)에서 내부 결과를 원하는 대상에 매핑

이로 인해 보호된 엔진 주위에 얇은 구성 계층이 추가되지만, MAGIC 트리거는 그대로 유지되어 복사/붙여넣기가 용이합니다.

대안

더 간단하지만 유지 관리가 어려운 접근 방식은 외부 참조를 코어 표현식에 직접 붙여 넣는 것입니다(예: 앱 변수 또는 table record field 자리 표시자를 인라인으로 사용).

이렇게 하면 추가적인 구성 트리거는 피할 수 있지만, 이식성이 떨어지고 매번 복사할 때마다 핵심 로직을 편집해야 하므로 재사용이 어려워집니다.

컴포넌트 배포의 경우, 내부 변수 인터페이스 패턴이 관리하기 더 쉽습니다. 입력과 출력은 에지에서 구성되는 반면, 계산 엔진은 안정적으로 보호됩니다.

MAGIC 트리거는 조회를 수행하고 아핀 변환을 적용합니다:

result = (value × factor) + offset

변환 정의가 내부 객체 목록에 중앙 집중화되어 있으므로, 트리거는 수정 없이 여러 경우를 처리할 수 있습니다.

이 구현에서는: * °C, K, °F가 지원됩니다 * 모든 3×3 조합이 미리 정의되어 있습니다 * 로직이 올바른 계수 및 오프셋을 동적으로 결정합니다

앱 빌더는 다음만 수행하면 됩니다: 1. 입력 소스 구성 2. 출력 대상 구성

수식 재작성 불필요. 조건부 로직 편집 불필요. 구조적 변경 불필요.

확장성이 뛰어난 이유

이 구조를 통해 다음이 가능합니다:* 객체 목록을 확장하여 새로운 단위 추가* 계산 엔진을 그대로 유지* 앱 전반에 걸친 아키텍처 일관성 유지* 여러 프로젝트에서 신속한 재사용

이 컴포넌트는 다음을 분리합니다:* 데이터 소스 구성 (입력 단계)* 실행 로직 (MAGIC)* 결과 라우팅 (출력 단계)

이러한 분리는 시스템을 안전하고, 예측 가능하며, 확장 가능하게 유지합니다.

트리거 분석 – MAGIC(표현식)의 작동 방식

보호된 MAGIC 트리거는 단일 표현식으로 구현됩니다:(@Variable._temperatureInputValue * array_value_at_index( map_to_number_list(@Variable._temperatureConversionDefaults , 'factor'), array_index_of( map_to_text_list(@Variable._temperatureConversionDefaults , 'inputUnit-targetUnit'), @Variable._temperatureInputUnit + '-' + @Variable._temperatureTargetUnit )))+array_value_at_index( map_to_number_list(@Variable._temperatureConversionDefaults , 'offset'), array_index_of( map_to_text_list(@Variable._temperatureConversionDefaults , 'inputUnit-targetUnit'), @Variable._temperatureInputUnit + '-' + @Variable._temperatureTargetUnit ))대략적으로 보면, 이 표현식은 다음 세 단계를 수행합니다:

1. 조회 키 생성

키는 구성된 입력 변수를 기반으로 동적으로 생성됩니다:

inputUnit-targetUnit

예를 들어:

°C-°F

이를 통해 로직이 범용성을 유지하며 지원되는 모든 조합에서 정상적으로 작동합니다.

2. 객체 목록에서 계수 및 오프셋 해결

이 표현식은 한 번의 처리 과정으로 계수(factor)와 오프셋(offset)을 구합니다:* map_to_text_list(..., 'inputUnit-targetUnit')은 조회 키 목록을 즉석에서 생성합니다.* array_index_of(...) 는 일치하는 키의 인덱스를 찾습니다.* array_value_at_index(...) 는 해당 인덱스를 사용하여 계수 및 오프셋 목록에서 대응하는 값을 가져옵니다.

여기서 핵심적인 설계 선택은 array_index_of(...)를 **map_to_text_list(...)**와 직접 결합하는 것입니다. 이를 통해 별도의 변수에 중간 키 목록을 저장하는 것을 피할 수 있습니다. 대신, 조회 목록은 인라인으로 생성되며 단일 표현식 내에서 팩터와 오프셋을 모두 검색하는 데 즉시 사용됩니다.

이를 통해 MAGIC 트리거는 자체적으로 완결성을 유지하며 구성 범위를 줄일 수 있고, 동시에 테이블이나 집계 로직 없이도 구조화된 조회와 같은 동작을 수행합니다.

3. 아핀 변환 적용

올바른 계수와 오프셋이 결정되면 계산이 적용됩니다:

result = (value × factor) + offset

조회 로직이 인덱스 기반이며 데이터 주도적이기 때문에, 이 트리거는 조건부 분기나 switch-case 구조가 필요하지 않습니다.

이 구조를 선택한 이유

  • 하드코딩된 조건부 로직을 피할 수 있습니다.
  • 새로운 단위 조합이 추가될 때 자연스럽게 확장됩니다.
  • 계산을 완전히 데이터 기반으로 유지합니다.
  • 여러 트리거 대신 단일 보호된 표현식을 유지합니다.

이 설계는 컴포넌트를 결정론적이고 확장 가능하며, 의도치 않은 논리 수정으로부터 안전하게 보호합니다.

새로운 단위로 확장하기

새로운 단위를 추가하려면 _temperatureConversionDefaults를 확장하여 다음을 포함하는 새 항목을 추가하십시오. * inputUnit-targetUnit (키) * 계수 * 오프셋

객체 목록에 새로운 매핑이 존재하면, 보호된 MAGIC 트리거가 이를 자동으로 해결할 수 있으며, 핵심 로직을 변경할 필요가 없습니다.

오류 처리 (안전한 조회 패턴)

MAGIC 트리거에는 변환 쌍을 찾을 수 없을 때 안전한 실행을 보장하기 위한 명시적인 오류 감지 기능이 포함되어 있습니다. 트리거는 빈 결과에 의존하는 대신 조회 인덱스를 직접 확인합니다:

ARRAY_INDEX_OF( MAP_TO_TEXT_LIST(_temperatureConversionDefaults, 'inputUnit-targetUnit'), _temperatureInputUnit + '-' + _temperatureTargetUnit )

반환된 인덱스가 -1인 경우, 일치하는 변환 규칙이 존재하지 않습니다.

실행 흐름: 1. 트리거 시작 시 _temperatureConversionResult를 초기화합니다. 2. 조회 인덱스 = -1인 경우 → 계산하지 않습니다. 3. 그 외의 경우 → 계산하고 결과를 저장합니다.

그런 다음 OUTPUT 단계에서 다음을 확인합니다:

_temperatureConversionResult가 비어 있으면 → 오류 메시지를 표시합니다.

예시 메시지:

"선택한 단위 쌍에 대한 변환 규칙을 찾을 수 없습니다. 단위 형식(°C, K, °F)을 확인하고, 해당 쌍이 _temperatureConversionDefaults에 존재하는지 확인하십시오."

이를 통해 다음을 보장합니다:• 이전 실행에서 남은 오래된 값이 남아 있지 않음 • 유효한 규칙이 없는 경우 계산이 시도되지 않음 • 사용자가 명확하고 실행 가능한 오류 메시지를 받음

핵심 로직은 보호된 상태로 유지되며 변경되지 않으면서도, 결정론적이고 안전한 동작을 제공합니다.

키, 계수, 오프셋을 별도의 배열로 분리하지 않는 이유는 무엇일까요?

대안적인 접근 방식은 다음과 같이 저장하는 것이었을 것입니다:* 조회 키를 하나의 배열에 저장* 계수를 다른 배열에 저장* 오프셋을 세 번째 배열에 저장

기술적으로는 작동하겠지만, 구조적인 위험이 발생합니다. 즉, 배열 간의 위치 관계가 항상 완벽하게 일치해야만 합니다.

한 배열의 순서가 바뀌고 다른 배열의 순서는 변하지 않으면, 키-계수-오프셋 간의 관계가 눈에 띄지 않게 깨져 잘못된 변환 결과가 발생하게 됩니다.

관련 속성을 객체 목록에 함께 저장함으로써, 각 항목은 입력 단위(inputUnit)-목표 단위(targetUnit), 계수, 오프셋 값을 논리적으로 그룹화된 상태로 유지합니다. 이는 정렬 불일치의 위험을 줄여주고, 구조를 더 안전하고 유지 관리하기 쉽게 만듭니다.

참여하기

커뮤니티에 참여하여 개선 사항을 공유하고, 추가 단위를 제안하며, 문제를 보고하거나 아키텍처 결정 사항에 대해 논의해 보세요.