Skip to content

Commit fb093f3

Browse files
committed
탐험 탭이 작은 행동을 아직 일일 미션이라고 부르던 것을 고침
스택을 띄워 탐험 탭을 열었더니 `오늘의 성장 효율` 카드가 같은 기능을 `일일 미션`이라고 부르고 있었다. 같은 파일 안의 다른 문구는 전부 `작은 행동`인데 이 한 줄만 옛 이름으로 남아 있었다. 앱에는 이런 어휘를 막는 검사가 있는데(`user_facing_vocabulary_test`) Dart 문자열만 훑는다. 서버가 내려보내는 라벨은 그 그물을 그냥 지나간다. 이 줄도 그래서 살아남았다. 서버 쪽에도 같은 그물을 친다. `server/app` 아래 파이썬을 AST로 훑어 `label`·`name`·`message`처럼 화면에 그대로 실려 나가는 키의 값만 본다. 주석과 docstring은 보지 않는다 - 화면에 나가는 것은 payload의 값이다. 라벨을 옛 이름으로 되돌려 놓고 돌려 보니 그 줄을 정확히 짚었다. 서버를 다시 띄워 응답도 확인했다 - 이제 `작은 행동`으로 나간다.
1 parent e9d3693 commit fb093f3

2 files changed

Lines changed: 71 additions & 1 deletion

File tree

server/app/services/adventure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ async def state_payload(db: AsyncSession, user_id: int) -> dict:
12431243
},
12441244
"economy": [
12451245
{"code": "diary", "label": "마음 일기", "exp": 40, "seeds": 15},
1246-
{"code": "quest", "label": "일일 미션", "exp": 20, "seeds": 5},
1246+
{"code": "quest", "label": "작은 행동", "exp": 20, "seeds": 5},
12471247
{"code": "dungeon", "label": "던전", "exp": 10, "seeds": 4},
12481248
{"code": "patrol", "label": "순찰", "exp": 0, "seeds": 3},
12491249
],
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""화면에 나가는 서버 문구가 옛 어휘를 쓰지 않는지 본다.
2+
3+
앱 쪽에는 같은 검사가 있는데(`app/test/user_facing_vocabulary_test.dart`)
4+
서버가 내려보내는 라벨은 그 그물에 안 걸린다. 실제로 탐험 탭의 `오늘의 성장
5+
효율`이 같은 기능을 `일일 미션`이라고 부르고 있었다 - 같은 파일의 다른 문구는
6+
전부 `작은 행동`인데 이 한 줄만 남아 있었다.
7+
8+
주석과 docstring은 보지 않는다. 화면에 나가는 것은 payload의 값이다.
9+
"""
10+
11+
from __future__ import annotations
12+
13+
import ast
14+
import pathlib
15+
16+
# 쓰면 안 되는 말과 대신 쓰는 말. 세계관이 정한 어휘다.
17+
BANNED = {
18+
"미션": "작은 행동",
19+
"퀘스트": "작은 행동",
20+
"몬스터": "엉킴 또는 수호자",
21+
"스킬북": "기록서",
22+
"레벨업": "성장",
23+
}
24+
25+
# 화면에 그대로 실려 나가는 키. 코드·상태값은 보지 않는다.
26+
USER_FACING_KEYS = {
27+
"label",
28+
"name",
29+
"message",
30+
"description",
31+
"caption",
32+
"title",
33+
"hint",
34+
"summary",
35+
"telegraph",
36+
"effect_summary",
37+
"unlock_hint",
38+
"lock_reason",
39+
"retired_reason",
40+
}
41+
42+
ROOT = pathlib.Path(__file__).resolve().parents[2] / "app"
43+
44+
45+
def _offenders() -> list[str]:
46+
found: list[str] = []
47+
for path in sorted(ROOT.rglob("*.py")):
48+
if "__pycache__" in path.parts:
49+
continue
50+
tree = ast.parse(path.read_text(encoding="utf-8"))
51+
for node in ast.walk(tree):
52+
if not isinstance(node, ast.Dict):
53+
continue
54+
for key, value in zip(node.keys, node.values):
55+
if not (isinstance(key, ast.Constant) and key.value in USER_FACING_KEYS):
56+
continue
57+
if not (isinstance(value, ast.Constant) and isinstance(value.value, str)):
58+
continue
59+
for word, instead in BANNED.items():
60+
if word in value.value:
61+
found.append(
62+
f"{path.name}:{value.lineno} {value.value!r} "
63+
f"→ {word} 대신 {instead}"
64+
)
65+
return found
66+
67+
68+
def test_server_labels_use_the_current_wording():
69+
offenders = _offenders()
70+
assert not offenders, "옛 어휘가 남아 있습니다:\n" + "\n".join(offenders)

0 commit comments

Comments
 (0)