From 9c57a39288177b86690abba5d54b014cd76cc376 Mon Sep 17 00:00:00 2001 From: hoshino-love0102 Date: Fri, 16 Oct 2026 23:39:20 +0900 Subject: [PATCH] Replace reviewed hairstyle catalogue photos through Actions --- .github/workflows/catalog-refresh.yml | 84 + catalog/curated-20261016/.gitignore | 1 + catalog/curated-20261016/apply.sql | 679 ++++ catalog/curated-20261016/manifest.json | 3963 ++++++++++++++++++++++++ scripts/catalog.py | 154 + scripts/catalog_test.py | 58 + 6 files changed, 4939 insertions(+) create mode 100644 .github/workflows/catalog-refresh.yml create mode 100644 catalog/curated-20261016/.gitignore create mode 100644 catalog/curated-20261016/apply.sql create mode 100644 catalog/curated-20261016/manifest.json create mode 100644 scripts/catalog.py create mode 100644 scripts/catalog_test.py diff --git a/.github/workflows/catalog-refresh.yml b/.github/workflows/catalog-refresh.yml new file mode 100644 index 00000000..6f712346 --- /dev/null +++ b/.github/workflows/catalog-refresh.yml @@ -0,0 +1,84 @@ +name: Refresh hairstyle catalogue + +on: + pull_request: + paths: + - 'catalog/**' + - 'scripts/catalog*.py' + - '.github/workflows/catalog-refresh.yml' + workflow_dispatch: + inputs: + apply: + description: Upload the reviewed images and activate the catalogue in production + type: boolean + default: false + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_USER: catalogue_test + POSTGRES_PASSWORD: disposable-test-password + POSTGRES_DB: catalogue_test + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U catalogue_test" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Validate reviewed images and classifications + run: python scripts/catalog.py catalog/curated-20261016 --fetch-images + - name: Test transaction and retry against current schema + env: + CATALOG_TEST_DATABASE_URL: postgresql://catalogue_test:disposable-test-password@localhost:5432/catalogue_test + run: python scripts/catalog_test.py catalog/curated-20261016 + + apply: + if: github.event_name == 'workflow_dispatch' && inputs.apply && github.ref == 'refs/heads/main' + needs: validate + runs-on: ubuntu-latest + concurrency: + group: deploy-production + cancel-in-progress: false + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Fetch and verify the reviewed original images + run: python scripts/catalog.py catalog/curated-20261016 --fetch-images + - name: Bundle compatible S3 SDK + run: python -m pip install --disable-pip-version-check --no-compile --target /tmp/catalogue-vendor boto3==1.40.0 + - name: Prepare SSH + env: + EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }} + EC2_HOST: ${{ secrets.EC2_HOST }} + EC2_PORT: ${{ secrets.EC2_PORT }} + run: | + mkdir -p ~/.ssh + printf '%s\n' "$EC2_SSH_KEY" > ~/.ssh/catalogue_key + chmod 600 ~/.ssh/catalogue_key + ssh-keyscan -p "$EC2_PORT" "$EC2_HOST" >> ~/.ssh/known_hosts + - name: Upload verified release and apply atomically + env: + EC2_HOST: ${{ secrets.EC2_HOST }} + EC2_PORT: ${{ secrets.EC2_PORT }} + EC2_USER: ${{ secrets.EC2_USER }} + run: | + remote_dir="/tmp/heddy-catalog-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + tar -czf /tmp/catalogue.tar.gz -C catalog curated-20261016 -C "$GITHUB_WORKSPACE/scripts" catalog.py -C /tmp catalogue-vendor + ssh -i ~/.ssh/catalogue_key -p "$EC2_PORT" "$EC2_USER@$EC2_HOST" "mkdir -m 700 '$remote_dir'" + scp -i ~/.ssh/catalogue_key -P "$EC2_PORT" /tmp/catalogue.tar.gz "$EC2_USER@$EC2_HOST:$remote_dir/release.tar.gz" + ssh -i ~/.ssh/catalogue_key -p "$EC2_PORT" "$EC2_USER@$EC2_HOST" "set -eu; cd '$remote_dir'; tar -xzf release.tar.gz; PYTHONPATH='$remote_dir/catalogue-vendor' python3 catalog.py curated-20261016 --apply" diff --git a/catalog/curated-20261016/.gitignore b/catalog/curated-20261016/.gitignore new file mode 100644 index 00000000..2ae923f8 --- /dev/null +++ b/catalog/curated-20261016/.gitignore @@ -0,0 +1 @@ +/images/ diff --git a/catalog/curated-20261016/apply.sql b/catalog/curated-20261016/apply.sql new file mode 100644 index 00000000..5e263bda --- /dev/null +++ b/catalog/curated-20261016/apply.sql @@ -0,0 +1,679 @@ +BEGIN; + +SET LOCAL lock_timeout = '10s'; + +SET LOCAL statement_timeout = '60s'; + +SELECT pg_advisory_xact_lock(hashtext('heddy-catalog-refresh')); + +DO $$ BEGIN IF (SELECT count(*) FROM hairstyle_assets a JOIN files f ON f.file_id=a.thumbnail_file_id WHERE a.hairstyle_id IN ('71000000-0000-4000-8000-000000000045'::uuid,'71000000-0000-4000-8000-000000000068'::uuid,'71000000-0000-4000-8000-000000000069'::uuid,'71000000-0000-4000-8000-000000000074'::uuid,'71000000-0000-4000-8000-000000000075'::uuid,'71000000-0000-4000-8000-000000000076'::uuid,'71000000-0000-4000-8000-000000000077'::uuid,'71000000-0000-4000-8000-000000000078'::uuid,'71000000-0000-4000-8000-000000000001'::uuid,'71000000-0000-4000-8000-000000000002'::uuid,'71000000-0000-4000-8000-000000000003'::uuid,'71000000-0000-4000-8000-000000000007'::uuid,'71000000-0000-4000-8000-000000000008'::uuid,'71000000-0000-4000-8000-000000000009'::uuid,'71000000-0000-4000-8000-000000000010'::uuid,'71000000-0000-4000-8000-000000000011'::uuid,'71000000-0000-4000-8000-000000000014'::uuid,'71000000-0000-4000-8000-000000000015'::uuid,'71000000-0000-4000-8000-000000000016'::uuid,'71000000-0000-4000-8000-000000000017'::uuid,'71000000-0000-4000-8000-000000000021'::uuid,'71000000-0000-4000-8000-000000000022'::uuid,'71000000-0000-4000-8000-000000000023'::uuid,'71000000-0000-4000-8000-000000000024'::uuid,'71000000-0000-4000-8000-000000000025'::uuid,'71000000-0000-4000-8000-000000000026'::uuid,'71000000-0000-4000-8000-000000000027'::uuid,'71000000-0000-4000-8000-000000000028'::uuid,'71000000-0000-4000-8000-000000000029'::uuid,'71000000-0000-4000-8000-000000000030'::uuid,'71000000-0000-4000-8000-000000000050'::uuid,'71000000-0000-4000-8000-000000000051'::uuid,'71000000-0000-4000-8000-000000000052'::uuid,'71000000-0000-4000-8000-000000000053'::uuid,'71000000-0000-4000-8000-000000000054'::uuid,'71000000-0000-4000-8000-000000000055'::uuid,'71000000-0000-4000-8000-000000000056'::uuid,'71000000-0000-4000-8000-000000000057'::uuid,'71000000-0000-4000-8000-000000000058'::uuid,'71000000-0000-4000-8000-000000000059'::uuid,'71000000-0000-4000-8000-000000000060'::uuid,'71000000-0000-4000-8000-000000000061'::uuid,'71000000-0000-4000-8000-000000000062'::uuid,'71000000-0000-4000-8000-000000000063'::uuid,'71000000-0000-4000-8000-000000000064'::uuid,'71000000-0000-4000-8000-000000000065'::uuid,'71000000-0000-4000-8000-000000000066'::uuid,'71000000-0000-4000-8000-000000000067'::uuid,'71000000-0000-4000-8000-000000000012'::uuid,'71000000-0000-4000-8000-000000000013'::uuid,'71000000-0000-4000-8000-000000000018'::uuid,'71000000-0000-4000-8000-000000000019'::uuid,'71000000-0000-4000-8000-000000000020'::uuid,'71000000-0000-4000-8000-000000000031'::uuid,'71000000-0000-4000-8000-000000000032'::uuid,'71000000-0000-4000-8000-000000000033'::uuid,'71000000-0000-4000-8000-000000000037'::uuid,'71000000-0000-4000-8000-000000000038'::uuid,'71000000-0000-4000-8000-000000000039'::uuid,'71000000-0000-4000-8000-000000000040'::uuid,'71000000-0000-4000-8000-000000000042'::uuid,'71000000-0000-4000-8000-000000000043'::uuid,'71000000-0000-4000-8000-000000000044'::uuid,'71000000-0000-4000-8000-000000000046'::uuid,'71000000-0000-4000-8000-000000000048'::uuid,'71000000-0000-4000-8000-000000000071'::uuid,'71000000-0000-4000-8000-000000000073'::uuid,'71000000-0000-4000-8000-000000000080'::uuid,'71000000-0000-4000-8000-000000000081'::uuid,'71000000-0000-4000-8000-000000000082'::uuid,'71000000-0000-4000-8000-000000000083'::uuid,'71000000-0000-4000-8000-000000000084'::uuid,'71000000-0000-4000-8000-000000000085'::uuid,'71000000-0000-4000-8000-000000000086'::uuid,'71000000-0000-4000-8000-000000000087'::uuid,'71000000-0000-4000-8000-000000000088'::uuid,'71000000-0000-4000-8000-000000000089'::uuid,'71000000-0000-4000-8000-000000000090'::uuid,'71000000-0000-4000-8000-000000000092'::uuid,'71000000-0000-4000-8000-000000000093'::uuid,'71000000-0000-4000-8000-000000000094'::uuid,'71000000-0000-4000-8000-000000000095'::uuid,'71000000-0000-4000-8000-000000000096'::uuid,'71000000-0000-4000-8000-000000000097'::uuid,'71000000-0000-4000-8000-000000000100'::uuid,'71000000-0000-4000-8000-000000000004'::uuid,'71000000-0000-4000-8000-000000000006'::uuid,'71000000-0000-4000-8000-000000000035'::uuid,'71000000-0000-4000-8000-000000000036'::uuid,'71000000-0000-4000-8000-000000000047'::uuid,'71000000-0000-4000-8000-000000000049'::uuid,'71000000-0000-4000-8000-000000000070'::uuid,'71000000-0000-4000-8000-000000000072'::uuid,'71000000-0000-4000-8000-000000000079'::uuid,'71000000-0000-4000-8000-000000000091'::uuid,'71000000-0000-4000-8000-000000000098'::uuid,'71000000-0000-4000-8000-000000000099'::uuid,'71000000-0000-4000-8000-000000000005'::uuid,'71000000-0000-4000-8000-000000000034'::uuid,'71000000-0000-4000-8000-000000000041'::uuid) AND f.owner_type='SYSTEM' AND f.purpose='HAIRSTYLE_THUMBNAIL') <> 100 THEN RAISE EXCEPTION 'Original SYSTEM catalogue does not match the reviewed 100 records'; END IF; END $$; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('daa307bc-d79f-56f6-9ec4-76d55ab60d84','eafbe55d-7dab-5cc3-b523-1f616851a0c2',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/daa307bc-d79f-56f6-9ec4-76d55ab60d84.jpg','image/jpeg',72941,'7cd22bb04afe2a027620e55ba246e9bcff19e5e39e930f1c3f8fd5c844a84f5b',1200,800,TIMESTAMPTZ '2100-01-01 00:00:00+00','하드파트 포마드컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='daa307bc-d79f-56f6-9ec4-76d55ab60d84' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/daa307bc-d79f-56f6-9ec4-76d55ab60d84.jpg' AND sha256='7cd22bb04afe2a027620e55ba246e9bcff19e5e39e930f1c3f8fd5c844a84f5b' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('0d198408-5bf7-514c-b3af-52d56d990361','하드파트 포마드컷','SHORT','daa307bc-d79f-56f6-9ec4-76d55ab60d84',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('0d198408-5bf7-514c-b3af-52d56d990361','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('31683b6d-c9ee-5478-8e0d-52d9a61e58ad','포마드컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('0d198408-5bf7-514c-b3af-52d56d990361','31683b6d-c9ee-5478-8e0d-52d9a61e58ad') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('44105ceb-49bd-507b-b1b0-2b83c7889e5e','사이드파트','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('0d198408-5bf7-514c-b3af-52d56d990361','44105ceb-49bd-507b-b1b0-2b83c7889e5e') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('0d9c8d3c-6b58-57fa-9764-f8967cb516d0','1dc5de0a-e214-58a9-85e4-e1c96cf4e934',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/0d9c8d3c-6b58-57fa-9764-f8967cb516d0.jpg','image/jpeg',204609,'718b928a8a0238fe6af5352ff303cffd0bec05c807f6ec0aefe02502fd9532db',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','라인 언더컷 퀴프.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='0d9c8d3c-6b58-57fa-9764-f8967cb516d0' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/0d9c8d3c-6b58-57fa-9764-f8967cb516d0.jpg' AND sha256='718b928a8a0238fe6af5352ff303cffd0bec05c807f6ec0aefe02502fd9532db' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('81871f24-b393-593a-b0b8-f31d7296118a','라인 언더컷 퀴프','SHORT','0d9c8d3c-6b58-57fa-9764-f8967cb516d0',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('81871f24-b393-593a-b0b8-f31d7296118a','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('73108c3a-e190-5c3f-afc8-6df41dc448f4','언더컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('81871f24-b393-593a-b0b8-f31d7296118a','73108c3a-e190-5c3f-afc8-6df41dc448f4') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('f5f3cdba-c2f7-535c-a6ed-3261d3b46b38','리젠트컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('81871f24-b393-593a-b0b8-f31d7296118a','f5f3cdba-c2f7-535c-a6ed-3261d3b46b38') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('b38860ec-2947-5bab-b2f5-f603b60f4296','d33891da-54c0-5dbc-a91b-5fef07b7a52f',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/b38860ec-2947-5bab-b2f5-f603b60f4296.jpg','image/jpeg',291875,'95aaf9d6b7b885d8187db8cf199f172ef41ad3871db29640a9311b5e56a4aa9a',1200,2133,TIMESTAMPTZ '2100-01-01 00:00:00+00','텍스처 크롭 페이드.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='b38860ec-2947-5bab-b2f5-f603b60f4296' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/b38860ec-2947-5bab-b2f5-f603b60f4296.jpg' AND sha256='95aaf9d6b7b885d8187db8cf199f172ef41ad3871db29640a9311b5e56a4aa9a' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('7ffbc247-3416-5316-905e-6fe769a4153f','텍스처 크롭 페이드','SHORT','b38860ec-2947-5bab-b2f5-f603b60f4296',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('7ffbc247-3416-5316-905e-6fe769a4153f','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2d264d93-04a6-51bd-b741-dfec5339ce53','크롭컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('7ffbc247-3416-5316-905e-6fe769a4153f','2d264d93-04a6-51bd-b741-dfec5339ce53') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('4a481ee2-aee6-5fb7-bbfb-8ef480608caf','페이드컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('7ffbc247-3416-5316-905e-6fe769a4153f','4a481ee2-aee6-5fb7-bbfb-8ef480608caf') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('b08e5ad3-825b-59b0-80ea-c16864f87666','35fea6d2-b11b-5755-8c7a-9f2f9d2e1236',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/b08e5ad3-825b-59b0-80ea-c16864f87666.jpg','image/jpeg',66203,'cac53507b351588dab19b86bb1337649ac781e17e49f459be060d17c5796fc3c',1200,800,TIMESTAMPTZ '2100-01-01 00:00:00+00','클래식 폼파두르.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='b08e5ad3-825b-59b0-80ea-c16864f87666' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/b08e5ad3-825b-59b0-80ea-c16864f87666.jpg' AND sha256='cac53507b351588dab19b86bb1337649ac781e17e49f459be060d17c5796fc3c' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('12d8e1fd-b70d-50e0-a0d5-a4d769759969','클래식 폼파두르','SHORT','b08e5ad3-825b-59b0-80ea-c16864f87666',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('12d8e1fd-b70d-50e0-a0d5-a4d769759969','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',15,'HIGH','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('31683b6d-c9ee-5478-8e0d-52d9a61e58ad','포마드컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('12d8e1fd-b70d-50e0-a0d5-a4d769759969','31683b6d-c9ee-5478-8e0d-52d9a61e58ad') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('f5f3cdba-c2f7-535c-a6ed-3261d3b46b38','리젠트컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('12d8e1fd-b70d-50e0-a0d5-a4d769759969','f5f3cdba-c2f7-535c-a6ed-3261d3b46b38') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('3a4285bc-72f7-5c24-a42b-5f6e8de43de0','60cd55cf-6f1a-57c1-a317-f15253bf74d8',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/3a4285bc-72f7-5c24-a42b-5f6e8de43de0.jpg','image/jpeg',255717,'4ae41d430b48f4c3ba0cd012c55662e65b7e1a586d78dd10487f828135595314',1200,1832,TIMESTAMPTZ '2100-01-01 00:00:00+00','하이 퀴프 페이드.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='3a4285bc-72f7-5c24-a42b-5f6e8de43de0' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/3a4285bc-72f7-5c24-a42b-5f6e8de43de0.jpg' AND sha256='4ae41d430b48f4c3ba0cd012c55662e65b7e1a586d78dd10487f828135595314' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('becfa223-4c43-5ca1-80b0-1e3cd17bf008','하이 퀴프 페이드','SHORT','3a4285bc-72f7-5c24-a42b-5f6e8de43de0',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('becfa223-4c43-5ca1-80b0-1e3cd17bf008','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('f5f3cdba-c2f7-535c-a6ed-3261d3b46b38','리젠트컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('becfa223-4c43-5ca1-80b0-1e3cd17bf008','f5f3cdba-c2f7-535c-a6ed-3261d3b46b38') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('4a481ee2-aee6-5fb7-bbfb-8ef480608caf','페이드컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('becfa223-4c43-5ca1-80b0-1e3cd17bf008','4a481ee2-aee6-5fb7-bbfb-8ef480608caf') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('9e42ce49-eaed-5b2d-8cea-9e25541ccfcc','b34630f0-9ebe-5a7b-adc3-b9621bee9bec',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/9e42ce49-eaed-5b2d-8cea-9e25541ccfcc.jpg','image/jpeg',158675,'ad553a0d085329b90b157a80f135bd31074d44b5610474831c3a03d95a6c94d1',1200,1600,TIMESTAMPTZ '2100-01-01 00:00:00+00','센터파트 볼륨 가르마.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='9e42ce49-eaed-5b2d-8cea-9e25541ccfcc' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/9e42ce49-eaed-5b2d-8cea-9e25541ccfcc.jpg' AND sha256='ad553a0d085329b90b157a80f135bd31074d44b5610474831c3a03d95a6c94d1' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('2f367c11-435d-5c1f-b005-45af3c693de2','센터파트 볼륨 가르마','SHORT','9e42ce49-eaed-5b2d-8cea-9e25541ccfcc',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('2f367c11-435d-5c1f-b005-45af3c693de2','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('122bdbdf-5ebf-5770-8c8a-c0c09e267343','가르마컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('2f367c11-435d-5c1f-b005-45af3c693de2','122bdbdf-5ebf-5770-8c8a-c0c09e267343') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('2f367c11-435d-5c1f-b005-45af3c693de2','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('a4de0c0c-d7db-5176-8b22-5c8a5069f52c','7adc82e6-acb0-5d0d-9d88-3132604861c0',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/a4de0c0c-d7db-5176-8b22-5c8a5069f52c.jpg','image/jpeg',155578,'c4a8614f3ef12d4ef1c3f9fad4cb136456263aed7f01a742345e6839a3d48b5a',1200,1600,TIMESTAMPTZ '2100-01-01 00:00:00+00','숄더 렝스 웨트 웨이브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='a4de0c0c-d7db-5176-8b22-5c8a5069f52c' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/a4de0c0c-d7db-5176-8b22-5c8a5069f52c.jpg' AND sha256='c4a8614f3ef12d4ef1c3f9fad4cb136456263aed7f01a742345e6839a3d48b5a' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('a04671ae-c01b-5568-8897-cea144960c1d','숄더 렝스 웨트 웨이브','MEDIUM','a4de0c0c-d7db-5176-8b22-5c8a5069f52c',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('a04671ae-c01b-5568-8897-cea144960c1d','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('a04671ae-c01b-5568-8897-cea144960c1d','40000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2c517e2b-986e-5bde-8744-9ade9b5f9a7d','웨이브','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('a04671ae-c01b-5568-8897-cea144960c1d','2c517e2b-986e-5bde-8744-9ade9b5f9a7d') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('14365489-043d-5b3d-9480-85cfdf1ab9e1','9ada00ac-de14-515e-99f0-5c90c9c82bca',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/14365489-043d-5b3d-9480-85cfdf1ab9e1.jpg','image/jpeg',274261,'14a1d4fa561c0ad657a004b0836a2d16b875e0eb44630d06d9fda2099dc91038',1200,2138,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드 스윕 숏컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='14365489-043d-5b3d-9480-85cfdf1ab9e1' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/14365489-043d-5b3d-9480-85cfdf1ab9e1.jpg' AND sha256='14a1d4fa561c0ad657a004b0836a2d16b875e0eb44630d06d9fda2099dc91038' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('3ae493ce-1c5c-566c-95a2-ca69ace18f5f','사이드 스윕 숏컷','SHORT','14365489-043d-5b3d-9480-85cfdf1ab9e1',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('3ae493ce-1c5c-566c-95a2-ca69ace18f5f','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('44105ceb-49bd-507b-b1b0-2b83c7889e5e','사이드파트','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('3ae493ce-1c5c-566c-95a2-ca69ace18f5f','44105ceb-49bd-507b-b1b0-2b83c7889e5e') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f','785ef37b-cb07-5ddc-93f8-4ea39f6037ec',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f.jpg','image/jpeg',173960,'639db8c8913cfe961c1890b1cc6844a6441238ab3989a71689696b58e4a428fd',1200,1707,TIMESTAMPTZ '2100-01-01 00:00:00+00','컬리 하이톱.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f.jpg' AND sha256='639db8c8913cfe961c1890b1cc6844a6441238ab3989a71689696b58e4a428fd' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('0c29ffac-9ee8-56b8-a7bf-13d46a0830a5','컬리 하이톱','SHORT','e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('0c29ffac-9ee8-56b8-a7bf-13d46a0830a5','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('0bece143-5b8e-50f8-b836-c698a5e9378e','컬리컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('0c29ffac-9ee8-56b8-a7bf-13d46a0830a5','0bece143-5b8e-50f8-b836-c698a5e9378e') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('f35d850d-e1e9-5889-8536-170c1b4dd860','c9c7c93e-5a05-5c2b-a8ce-f108c0fe20b4',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/f35d850d-e1e9-5889-8536-170c1b4dd860.jpg','image/jpeg',119849,'762bfc399c1f086e30036a40fc1c89b1b273485b328c0e0a8b81ce38ab3075e8',1200,1500,TIMESTAMPTZ '2100-01-01 00:00:00+00','내추럴 아프로.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='f35d850d-e1e9-5889-8536-170c1b4dd860' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/f35d850d-e1e9-5889-8536-170c1b4dd860.jpg' AND sha256='762bfc399c1f086e30036a40fc1c89b1b273485b328c0e0a8b81ce38ab3075e8' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('3719ea70-8240-5652-8a16-02f8f5685b5f','내추럴 아프로','SHORT','f35d850d-e1e9-5889-8536-170c1b4dd860',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('3719ea70-8240-5652-8a16-02f8f5685b5f','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('6d25f1f6-498a-51b9-b88a-b9b937a6c1bd','아프로','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('3719ea70-8240-5652-8a16-02f8f5685b5f','6d25f1f6-498a-51b9-b88a-b9b937a6c1bd') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('251f0cfd-e52c-5d25-b760-afcb97ef0211','9f567933-7385-59e0-b97f-caf3ba3deacc',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/251f0cfd-e52c-5d25-b760-afcb97ef0211.jpg','image/jpeg',137001,'9b43790775b604fc4f67c6a2e27533b373c09420cb82879e69abf9b83f0f300d',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','컬리 프린지 테이퍼.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='251f0cfd-e52c-5d25-b760-afcb97ef0211' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/251f0cfd-e52c-5d25-b760-afcb97ef0211.jpg' AND sha256='9b43790775b604fc4f67c6a2e27533b373c09420cb82879e69abf9b83f0f300d' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('55a650e5-5a99-5ed7-bd78-2ede7a818ed6','컬리 프린지 테이퍼','SHORT','251f0cfd-e52c-5d25-b760-afcb97ef0211',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('55a650e5-5a99-5ed7-bd78-2ede7a818ed6','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('0bece143-5b8e-50f8-b836-c698a5e9378e','컬리컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('55a650e5-5a99-5ed7-bd78-2ede7a818ed6','0bece143-5b8e-50f8-b836-c698a5e9378e') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('143f8cd1-e08f-5cc5-8c23-8838463620b5','테이퍼컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('55a650e5-5a99-5ed7-bd78-2ede7a818ed6','143f8cd1-e08f-5cc5-8c23-8838463620b5') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('9bd9a927-911f-5afb-8777-6c32d6ee9aa5','54c0e743-8f32-5c08-82f2-1cda777a6f2a',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/9bd9a927-911f-5afb-8777-6c32d6ee9aa5.jpg','image/jpeg',259470,'21b6e777221880b6df28df3ee160fd88ffc74ef85a9b2408b6685123ccd54832',1200,1509,TIMESTAMPTZ '2100-01-01 00:00:00+00','웨이브 퀴프 언더컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='9bd9a927-911f-5afb-8777-6c32d6ee9aa5' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/9bd9a927-911f-5afb-8777-6c32d6ee9aa5.jpg' AND sha256='21b6e777221880b6df28df3ee160fd88ffc74ef85a9b2408b6685123ccd54832' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('f44a00c9-3c3f-5927-98f7-32966c2b137e','웨이브 퀴프 언더컷','SHORT','9bd9a927-911f-5afb-8777-6c32d6ee9aa5',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('f44a00c9-3c3f-5927-98f7-32966c2b137e','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('73108c3a-e190-5c3f-afc8-6df41dc448f4','언더컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('f44a00c9-3c3f-5927-98f7-32966c2b137e','73108c3a-e190-5c3f-afc8-6df41dc448f4') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2c517e2b-986e-5bde-8744-9ade9b5f9a7d','웨이브','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('f44a00c9-3c3f-5927-98f7-32966c2b137e','2c517e2b-986e-5bde-8744-9ade9b5f9a7d') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('8bf0e3b5-0402-5137-a403-03548286b0b6','f0904534-2e9e-556d-a986-5bf6ff5b1fe7',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/8bf0e3b5-0402-5137-a403-03548286b0b6.jpg','image/jpeg',280909,'2650054181fa1ed677aaceecccaaf941a73ef2cd8e6da215fc35a26074416472',1200,1783,TIMESTAMPTZ '2100-01-01 00:00:00+00','로우 페이드 사이드파트.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='8bf0e3b5-0402-5137-a403-03548286b0b6' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/8bf0e3b5-0402-5137-a403-03548286b0b6.jpg' AND sha256='2650054181fa1ed677aaceecccaaf941a73ef2cd8e6da215fc35a26074416472' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('090392ec-2fcb-56e3-ba4c-0063af2471d3','로우 페이드 사이드파트','SHORT','8bf0e3b5-0402-5137-a403-03548286b0b6',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('090392ec-2fcb-56e3-ba4c-0063af2471d3','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('4a481ee2-aee6-5fb7-bbfb-8ef480608caf','페이드컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('090392ec-2fcb-56e3-ba4c-0063af2471d3','4a481ee2-aee6-5fb7-bbfb-8ef480608caf') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('44105ceb-49bd-507b-b1b0-2b83c7889e5e','사이드파트','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('090392ec-2fcb-56e3-ba4c-0063af2471d3','44105ceb-49bd-507b-b1b0-2b83c7889e5e') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('790da07c-fb25-54ae-ad32-463ce2dbde3d','7ec1675f-2cdf-5fdb-8dd0-1ed72c348b14',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/790da07c-fb25-54ae-ad32-463ce2dbde3d.jpg','image/jpeg',191746,'7c8b840b273b0cddc88eef441feec04e51b7db1d5ef8a4ee74aa2f18fea09122',1200,1766,TIMESTAMPTZ '2100-01-01 00:00:00+00','텍스처 멀릿컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='790da07c-fb25-54ae-ad32-463ce2dbde3d' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/790da07c-fb25-54ae-ad32-463ce2dbde3d.jpg' AND sha256='7c8b840b273b0cddc88eef441feec04e51b7db1d5ef8a4ee74aa2f18fea09122' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('30482fbb-adf7-5843-a988-f340b85fa429','텍스처 멀릿컷','MEDIUM','790da07c-fb25-54ae-ad32-463ce2dbde3d',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('30482fbb-adf7-5843-a988-f340b85fa429','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('3e2e412d-ee7a-5e5e-b9ae-ad080ca70269','멀릿컷','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('30482fbb-adf7-5843-a988-f340b85fa429','3e2e412d-ee7a-5e5e-b9ae-ad080ca70269') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('30482fbb-adf7-5843-a988-f340b85fa429','10000000-0000-4000-8000-000000000004') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('039a728d-7d09-53b4-840f-96e1411c62cf','505d88ac-a231-56fc-9346-41c2929675b8',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/039a728d-7d09-53b4-840f-96e1411c62cf.jpg','image/jpeg',185320,'b167b17eda6f2a2e329c0df9b23301feba7cfba80d474038f3c1079c977b8e94',1200,1799,TIMESTAMPTZ '2100-01-01 00:00:00+00','브러시백 멀릿컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='039a728d-7d09-53b4-840f-96e1411c62cf' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/039a728d-7d09-53b4-840f-96e1411c62cf.jpg' AND sha256='b167b17eda6f2a2e329c0df9b23301feba7cfba80d474038f3c1079c977b8e94' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('cb9713ac-a1b0-5e95-a303-e24bc4a425ac','브러시백 멀릿컷','MEDIUM','039a728d-7d09-53b4-840f-96e1411c62cf',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('cb9713ac-a1b0-5e95-a303-e24bc4a425ac','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('3e2e412d-ee7a-5e5e-b9ae-ad080ca70269','멀릿컷','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('cb9713ac-a1b0-5e95-a303-e24bc4a425ac','3e2e412d-ee7a-5e5e-b9ae-ad080ca70269') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('6fb84335-f266-5528-8c37-6981a17aa42c','15e79121-5f0d-5114-a099-acbb6552ad82',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/6fb84335-f266-5528-8c37-6981a17aa42c.jpg','image/jpeg',243852,'d74b29d9a7e1a27ffd58aac942d3222b459d9a6fa93c4510d2003b8e0a565c4c',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','미디엄 언더컷 웨이브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='6fb84335-f266-5528-8c37-6981a17aa42c' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/6fb84335-f266-5528-8c37-6981a17aa42c.jpg' AND sha256='d74b29d9a7e1a27ffd58aac942d3222b459d9a6fa93c4510d2003b8e0a565c4c' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('7d3fb671-6ae0-5bcb-a9f7-54d7c5f534f9','미디엄 언더컷 웨이브','MEDIUM','6fb84335-f266-5528-8c37-6981a17aa42c',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('7d3fb671-6ae0-5bcb-a9f7-54d7c5f534f9','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('73108c3a-e190-5c3f-afc8-6df41dc448f4','언더컷','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('7d3fb671-6ae0-5bcb-a9f7-54d7c5f534f9','73108c3a-e190-5c3f-afc8-6df41dc448f4') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2c517e2b-986e-5bde-8744-9ade9b5f9a7d','웨이브','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('7d3fb671-6ae0-5bcb-a9f7-54d7c5f534f9','2c517e2b-986e-5bde-8744-9ade9b5f9a7d') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('e0c83e60-8516-5dc7-b268-8180176c8196','42f61913-29d9-5d7f-95ad-cc7bceab6d0c',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/e0c83e60-8516-5dc7-b268-8180176c8196.jpg','image/jpeg',201305,'f8e07a2f52fda943a20f947540fa6069f49713bf46af627d0a4c0a29a881dbad',1200,2133,TIMESTAMPTZ '2100-01-01 00:00:00+00','버즈컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='e0c83e60-8516-5dc7-b268-8180176c8196' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/e0c83e60-8516-5dc7-b268-8180176c8196.jpg' AND sha256='f8e07a2f52fda943a20f947540fa6069f49713bf46af627d0a4c0a29a881dbad' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('b98199c9-df22-5914-8d6c-ff1c9818aa2e','버즈컷','SHORT','e0c83e60-8516-5dc7-b268-8180176c8196',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('b98199c9-df22-5914-8d6c-ff1c9818aa2e','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',0,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('0f3a9f4e-5dd8-59e7-99b1-a5657d3b35d0','버즈컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('b98199c9-df22-5914-8d6c-ff1c9818aa2e','0f3a9f4e-5dd8-59e7-99b1-a5657d3b35d0') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('cbc58c96-9253-5e84-a062-8c824986b638','79b782ca-78e9-5f5b-9743-01ecf7c8842e',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/cbc58c96-9253-5e84-a062-8c824986b638.jpg','image/jpeg',170500,'b79506f4a531bbcf9d144b4324b1149c354c34a3652c42abd4184f95e205eb94',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','크루컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='cbc58c96-9253-5e84-a062-8c824986b638' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/cbc58c96-9253-5e84-a062-8c824986b638.jpg' AND sha256='b79506f4a531bbcf9d144b4324b1149c354c34a3652c42abd4184f95e205eb94' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('7f17a8fe-2b82-558e-969f-79a3563ce8fb','크루컷','SHORT','cbc58c96-9253-5e84-a062-8c824986b638',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('7f17a8fe-2b82-558e-969f-79a3563ce8fb','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('17dacfcb-659b-5910-bb6d-11b242ac6579','크루컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('7f17a8fe-2b82-558e-969f-79a3563ce8fb','17dacfcb-659b-5910-bb6d-11b242ac6579') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('805dcd76-e71c-5aa6-bbed-6dd100f64ec2','3139fd32-2fac-5c2e-9d11-a8e5d2c05c7e',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/805dcd76-e71c-5aa6-bbed-6dd100f64ec2.jpg','image/jpeg',151545,'a61758d67be3a0238c10dd6e62eab0e4ee0cc80f6c2c959eff8a41998d490f20',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','스킨 페이드 버즈컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='805dcd76-e71c-5aa6-bbed-6dd100f64ec2' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/805dcd76-e71c-5aa6-bbed-6dd100f64ec2.jpg' AND sha256='a61758d67be3a0238c10dd6e62eab0e4ee0cc80f6c2c959eff8a41998d490f20' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('87408dfc-91cf-53df-bfab-369e62e93e38','스킨 페이드 버즈컷','SHORT','805dcd76-e71c-5aa6-bbed-6dd100f64ec2',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('87408dfc-91cf-53df-bfab-369e62e93e38','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY", "CURLY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',0,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('0f3a9f4e-5dd8-59e7-99b1-a5657d3b35d0','버즈컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('87408dfc-91cf-53df-bfab-369e62e93e38','0f3a9f4e-5dd8-59e7-99b1-a5657d3b35d0') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('4a481ee2-aee6-5fb7-bbfb-8ef480608caf','페이드컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('87408dfc-91cf-53df-bfab-369e62e93e38','4a481ee2-aee6-5fb7-bbfb-8ef480608caf') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('ccdd16a6-c616-548c-8c54-2e0fe6c6f167','1421bae9-6b73-5762-90da-f6d7e9062268',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/ccdd16a6-c616-548c-8c54-2e0fe6c6f167.jpg','image/jpeg',102651,'c47f24bcbb5d89291a4af08c67fd5c154cce7440a8096caa361f06514ebce693',1200,794,TIMESTAMPTZ '2100-01-01 00:00:00+00','투블록 보울컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='ccdd16a6-c616-548c-8c54-2e0fe6c6f167' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/ccdd16a6-c616-548c-8c54-2e0fe6c6f167.jpg' AND sha256='c47f24bcbb5d89291a4af08c67fd5c154cce7440a8096caa361f06514ebce693' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('b4b3e4fc-cd3e-5df2-ae97-4947e1076293','투블록 보울컷','SHORT','ccdd16a6-c616-548c-8c54-2e0fe6c6f167',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('b4b3e4fc-cd3e-5df2-ae97-4947e1076293','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('9a1ab3a1-dad0-505a-87fb-ccfea4f421e4','투블록','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('b4b3e4fc-cd3e-5df2-ae97-4947e1076293','9a1ab3a1-dad0-505a-87fb-ccfea4f421e4') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('24f5486e-0ce5-591a-a061-6f45e5ebb9e6','보울컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('b4b3e4fc-cd3e-5df2-ae97-4947e1076293','24f5486e-0ce5-591a-a061-6f45e5ebb9e6') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('ad79a8cf-d74f-504e-ae76-b31e24155234','1e7208fe-9db6-50f4-86ff-6fc46edac019',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/ad79a8cf-d74f-504e-ae76-b31e24155234.jpg','image/jpeg',98689,'1ea8a2d8ef6657569f9320cd395c1213f779bbfa60dda2e59727c5a1733b89e0',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','클래식 사이드파트.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='ad79a8cf-d74f-504e-ae76-b31e24155234' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/ad79a8cf-d74f-504e-ae76-b31e24155234.jpg' AND sha256='1ea8a2d8ef6657569f9320cd395c1213f779bbfa60dda2e59727c5a1733b89e0' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('b252732a-2d91-5c01-afd5-6d36f1bd3a92','클래식 사이드파트','SHORT','ad79a8cf-d74f-504e-ae76-b31e24155234',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('b252732a-2d91-5c01-afd5-6d36f1bd3a92','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('44105ceb-49bd-507b-b1b0-2b83c7889e5e','사이드파트','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('b252732a-2d91-5c01-afd5-6d36f1bd3a92','44105ceb-49bd-507b-b1b0-2b83c7889e5e') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('fe0565fa-297c-513b-80a1-111dde14aa81','8402b338-e9c2-544b-a054-1aa85685c10e',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/fe0565fa-297c-513b-80a1-111dde14aa81.jpg','image/jpeg',150811,'bd62355f481bbd647443739acf1e32ac84774039d088e4f449c1ab288c635f09',1200,1680,TIMESTAMPTZ '2100-01-01 00:00:00+00','스파이키 언더컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='fe0565fa-297c-513b-80a1-111dde14aa81' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/fe0565fa-297c-513b-80a1-111dde14aa81.jpg' AND sha256='bd62355f481bbd647443739acf1e32ac84774039d088e4f449c1ab288c635f09' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('86c65822-8ea7-58c4-b115-d9924435ea68','스파이키 언더컷','SHORT','fe0565fa-297c-513b-80a1-111dde14aa81',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('86c65822-8ea7-58c4-b115-d9924435ea68','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('73108c3a-e190-5c3f-afc8-6df41dc448f4','언더컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('86c65822-8ea7-58c4-b115-d9924435ea68','73108c3a-e190-5c3f-afc8-6df41dc448f4') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('d6310cdc-9828-5180-9eb3-aa8f35f91280','스파이키컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('86c65822-8ea7-58c4-b115-d9924435ea68','d6310cdc-9828-5180-9eb3-aa8f35f91280') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('4807dce0-83fe-53f6-87f2-3679c38cfd1b','b84fe71b-42dd-540f-98d4-e2cd61b7b630',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/4807dce0-83fe-53f6-87f2-3679c38cfd1b.jpg','image/jpeg',137009,'c3188ef87dc380f15d6ae680e51ab251f313b53a64400c84b657321cd65b984c',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','슬릭백 스타일.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='4807dce0-83fe-53f6-87f2-3679c38cfd1b' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/4807dce0-83fe-53f6-87f2-3679c38cfd1b.jpg' AND sha256='c3188ef87dc380f15d6ae680e51ab251f313b53a64400c84b657321cd65b984c' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('8a9d43c6-cf38-5cc3-b038-94fe0f689de7','슬릭백 스타일','SHORT','4807dce0-83fe-53f6-87f2-3679c38cfd1b',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('8a9d43c6-cf38-5cc3-b038-94fe0f689de7','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('01599c69-c392-5270-8080-8397be66c3c2','슬릭백','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('8a9d43c6-cf38-5cc3-b038-94fe0f689de7','01599c69-c392-5270-8080-8397be66c3c2') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4','b7784df8-a82e-5c32-8eda-8d6de7315471',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4.jpg','image/jpeg',232465,'c9860ced9e54ed4d8e0da5d4e98b315fa9e8a1cb8a0319da98b5f3f32e4af79e',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','센터파트 내추럴 플로우.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4.jpg' AND sha256='c9860ced9e54ed4d8e0da5d4e98b315fa9e8a1cb8a0319da98b5f3f32e4af79e' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('c17d60af-6144-5d44-9658-eda2e5ad638d','센터파트 내추럴 플로우','MEDIUM','8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('c17d60af-6144-5d44-9658-eda2e5ad638d','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('122bdbdf-5ebf-5770-8c8a-c0c09e267343','가르마컷','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('c17d60af-6144-5d44-9658-eda2e5ad638d','122bdbdf-5ebf-5770-8c8a-c0c09e267343') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('c17d60af-6144-5d44-9658-eda2e5ad638d','20000000-0000-4000-8000-000000000002') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c','246e7322-0fa8-5d64-a199-7d95950ad88c',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c.jpg','image/jpeg',45784,'ad8d88b85e804ce6885f142592c4d910bcaf4877760e8670356297e82556f28f',1200,800,TIMESTAMPTZ '2100-01-01 00:00:00+00','커튼뱅 리프컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c.jpg' AND sha256='ad8d88b85e804ce6885f142592c4d910bcaf4877760e8670356297e82556f28f' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('06cc4930-e540-5d6f-b284-1e08b1818a1c','커튼뱅 리프컷','MEDIUM','6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('06cc4930-e540-5d6f-b284-1e08b1818a1c','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('06cc4930-e540-5d6f-b284-1e08b1818a1c','20000000-0000-4000-8000-000000000002') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('06cc4930-e540-5d6f-b284-1e08b1818a1c','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('8da7f475-29f4-5218-9572-f1bc81b89985','d39c2916-e45c-5ac8-97a2-847a0731519b',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/8da7f475-29f4-5218-9572-f1bc81b89985.jpg','image/jpeg',204630,'a60c0ebd24d1ef0726b65b5e50ef212b4b3fbe1b4f03beae2cb355b41de1bf30',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','옴브레 롱 글램 웨이브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='8da7f475-29f4-5218-9572-f1bc81b89985' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/8da7f475-29f4-5218-9572-f1bc81b89985.jpg' AND sha256='a60c0ebd24d1ef0726b65b5e50ef212b4b3fbe1b4f03beae2cb355b41de1bf30' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('76145924-61ee-52ca-8fb6-faf5392da1d5','옴브레 롱 글램 웨이브','LONG','8da7f475-29f4-5218-9572-f1bc81b89985',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('76145924-61ee-52ca-8fb6-faf5392da1d5','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('76145924-61ee-52ca-8fb6-faf5392da1d5','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('76145924-61ee-52ca-8fb6-faf5392da1d5','50000000-0000-4000-8000-000000000004') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('830fef5a-219b-5a3a-894a-9c501d7f2ee4','43444ef5-7aad-5467-9f51-5e8b5a2203ee',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/830fef5a-219b-5a3a-894a-9c501d7f2ee4.jpg','image/jpeg',159658,'78b483d2523615d352db726eb7d70ac35142265d934256695c2f48076be56fcf',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드뱅 롱 레이어드.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='830fef5a-219b-5a3a-894a-9c501d7f2ee4' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/830fef5a-219b-5a3a-894a-9c501d7f2ee4.jpg' AND sha256='78b483d2523615d352db726eb7d70ac35142265d934256695c2f48076be56fcf' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('447ef348-7a08-5565-b345-de7ba9aa77fd','사이드뱅 롱 레이어드','LONG','830fef5a-219b-5a3a-894a-9c501d7f2ee4',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('447ef348-7a08-5565-b345-de7ba9aa77fd','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('447ef348-7a08-5565-b345-de7ba9aa77fd','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('447ef348-7a08-5565-b345-de7ba9aa77fd','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('ed1707b3-54c0-59a0-b921-f80d7d84beca','19711382-8e2e-585e-94cb-4a692c94b97d',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/ed1707b3-54c0-59a0-b921-f80d7d84beca.jpg','image/jpeg',251228,'8660c7d1dbadccb968483b72fe6e1b37ba016c39621d00ef3994c37d9d6fcf62',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','발레아쥬 중단발 웨이브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='ed1707b3-54c0-59a0-b921-f80d7d84beca' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/ed1707b3-54c0-59a0-b921-f80d7d84beca.jpg' AND sha256='8660c7d1dbadccb968483b72fe6e1b37ba016c39621d00ef3994c37d9d6fcf62' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('cbf47ccd-6a33-5cc1-963d-6dcd61fd0189','발레아쥬 중단발 웨이브','MEDIUM','ed1707b3-54c0-59a0-b921-f80d7d84beca',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('cbf47ccd-6a33-5cc1-963d-6dcd61fd0189','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('cbf47ccd-6a33-5cc1-963d-6dcd61fd0189','40000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2c517e2b-986e-5bde-8744-9ade9b5f9a7d','웨이브','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('cbf47ccd-6a33-5cc1-963d-6dcd61fd0189','2c517e2b-986e-5bde-8744-9ade9b5f9a7d') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('0aa49435-f662-5367-9c31-f2d70c4cbf18','d49ffc6f-8f9d-5181-a1c4-983910a11f9d',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/0aa49435-f662-5367-9c31-f2d70c4cbf18.jpg','image/jpeg',338540,'6cb04d4009bccb8ba729f368320ab46a9e34186b7ec577efdbe3a4feb18b5931',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','페이스프레이밍 레이어드.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='0aa49435-f662-5367-9c31-f2d70c4cbf18' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/0aa49435-f662-5367-9c31-f2d70c4cbf18.jpg' AND sha256='6cb04d4009bccb8ba729f368320ab46a9e34186b7ec577efdbe3a4feb18b5931' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('02310a32-527e-599b-aafd-a64c63398d68','페이스프레이밍 레이어드','LONG','0aa49435-f662-5367-9c31-f2d70c4cbf18',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('02310a32-527e-599b-aafd-a64c63398d68','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('02310a32-527e-599b-aafd-a64c63398d68','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('02310a32-527e-599b-aafd-a64c63398d68','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('12eea25f-cad4-59ce-9f21-18a8f268adc2','edb50534-704a-530e-9cdf-190477b5c48b',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/12eea25f-cad4-59ce-9f21-18a8f268adc2.jpg','image/jpeg',195920,'fc5e36eb6318655c3f440677e040d8390258759ddb73031960ad2c7624d4661b',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','커튼뱅 롱 스트레이트.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='12eea25f-cad4-59ce-9f21-18a8f268adc2' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/12eea25f-cad4-59ce-9f21-18a8f268adc2.jpg' AND sha256='fc5e36eb6318655c3f440677e040d8390258759ddb73031960ad2c7624d4661b' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('4972ab0a-e635-58d1-85e3-9889c6cce4a7','커튼뱅 롱 스트레이트','LONG','12eea25f-cad4-59ce-9f21-18a8f268adc2',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('4972ab0a-e635-58d1-85e3-9889c6cce4a7','["CUT", "STYLING"]'::jsonb,'["BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHEST',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('4972ab0a-e635-58d1-85e3-9889c6cce4a7','50000000-0000-4000-8000-000000000002') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('4972ab0a-e635-58d1-85e3-9889c6cce4a7','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('cc7d31a0-a676-5d7c-a8d6-ee960a834587','82fd3751-228a-522e-a492-276ef3dec3f3',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/cc7d31a0-a676-5d7c-a8d6-ee960a834587.jpg','image/jpeg',167927,'bce3e0bbc5aa0acef47f2707a162a40b41074f2c64e8025f57612cb7f78f3c39',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','블런트 프린지 픽시.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='cc7d31a0-a676-5d7c-a8d6-ee960a834587' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/cc7d31a0-a676-5d7c-a8d6-ee960a834587.jpg' AND sha256='bce3e0bbc5aa0acef47f2707a162a40b41074f2c64e8025f57612cb7f78f3c39' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('42d2fb31-6285-5aa4-864f-8fbe10e75a5c','블런트 프린지 픽시','SHORT','cc7d31a0-a676-5d7c-a8d6-ee960a834587',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('42d2fb31-6285-5aa4-864f-8fbe10e75a5c','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('42d2fb31-6285-5aa4-864f-8fbe10e75a5c','20000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('42d2fb31-6285-5aa4-864f-8fbe10e75a5c','10000000-0000-4000-8000-000000000004') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('d37fc871-3166-573f-bd37-579e9fd9d993','f4191642-47a1-5369-8e2c-541ed28ad0ff',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/d37fc871-3166-573f-bd37-579e9fd9d993.jpg','image/jpeg',219156,'7beae5de025bf361346eef67d276513ef303d49882432601a5c4e79e974aa8bf',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','볼륨 텍스처 픽시.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='d37fc871-3166-573f-bd37-579e9fd9d993' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/d37fc871-3166-573f-bd37-579e9fd9d993.jpg' AND sha256='7beae5de025bf361346eef67d276513ef303d49882432601a5c4e79e974aa8bf' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('f055858a-07fe-5a79-a257-b7ccc3dab7f8','볼륨 텍스처 픽시','SHORT','d37fc871-3166-573f-bd37-579e9fd9d993',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('f055858a-07fe-5a79-a257-b7ccc3dab7f8','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('f055858a-07fe-5a79-a257-b7ccc3dab7f8','20000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('c1d14fa0-b538-5746-84bd-0ccaf03a54f0','0404e286-42d3-5a11-8a27-4ebb523c9daf',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/c1d14fa0-b538-5746-84bd-0ccaf03a54f0.jpg','image/jpeg',126254,'12695486bf01e632a74f2a83aa2ff16c261e6bf766209cde39a2213d43486cf8',1200,1600,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드 프린지 픽시.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='c1d14fa0-b538-5746-84bd-0ccaf03a54f0' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/c1d14fa0-b538-5746-84bd-0ccaf03a54f0.jpg' AND sha256='12695486bf01e632a74f2a83aa2ff16c261e6bf766209cde39a2213d43486cf8' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('8167d07c-eb7e-5632-878a-0df3e0ac5ee7','사이드 프린지 픽시','SHORT','c1d14fa0-b538-5746-84bd-0ccaf03a54f0',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('8167d07c-eb7e-5632-878a-0df3e0ac5ee7','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('8167d07c-eb7e-5632-878a-0df3e0ac5ee7','20000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('8167d07c-eb7e-5632-878a-0df3e0ac5ee7','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('1f8d371f-ba06-5d7d-9425-d5afe5a0c36a','098cfb06-257a-5740-b3c9-8d0af011a90d',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/1f8d371f-ba06-5d7d-9425-d5afe5a0c36a.jpg','image/jpeg',180375,'728482538e48a69a5ae57f398314f8fb41c58401c2489ee3c7178cbab0cd0c38',1200,1600,TIMESTAMPTZ '2100-01-01 00:00:00+00','마이크로 프린지 숏컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='1f8d371f-ba06-5d7d-9425-d5afe5a0c36a' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/1f8d371f-ba06-5d7d-9425-d5afe5a0c36a.jpg' AND sha256='728482538e48a69a5ae57f398314f8fb41c58401c2489ee3c7178cbab0cd0c38' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('6abf9427-67ac-5754-a2f3-a29c50a8f4b6','마이크로 프린지 숏컷','SHORT','1f8d371f-ba06-5d7d-9425-d5afe5a0c36a',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('6abf9427-67ac-5754-a2f3-a29c50a8f4b6','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('6abf9427-67ac-5754-a2f3-a29c50a8f4b6','20000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('6abf9427-67ac-5754-a2f3-a29c50a8f4b6','10000000-0000-4000-8000-000000000004') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('6d90b2d5-8a82-5966-b7af-60c76f6a8525','5013a16d-a2d8-5292-975c-17bd8373dfa3',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/6d90b2d5-8a82-5966-b7af-60c76f6a8525.jpg','image/jpeg',224126,'ae0e8b2ba3ce49268254c6758b04428c9a9ce06ca75ff2ad2efeb8ec763985a1',1200,1799,TIMESTAMPTZ '2100-01-01 00:00:00+00','비대칭 언더컷 픽시.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='6d90b2d5-8a82-5966-b7af-60c76f6a8525' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/6d90b2d5-8a82-5966-b7af-60c76f6a8525.jpg' AND sha256='ae0e8b2ba3ce49268254c6758b04428c9a9ce06ca75ff2ad2efeb8ec763985a1' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('b1fdcdba-f935-5f0d-8fba-e9b56f3003fc','비대칭 언더컷 픽시','SHORT','6d90b2d5-8a82-5966-b7af-60c76f6a8525',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('b1fdcdba-f935-5f0d-8fba-e9b56f3003fc','["CUT", "STYLING"]'::jsonb,'["SHORT", "BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'SHORT',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('b1fdcdba-f935-5f0d-8fba-e9b56f3003fc','20000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('73108c3a-e190-5c3f-afc8-6df41dc448f4','언더컷','SHORT') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('b1fdcdba-f935-5f0d-8fba-e9b56f3003fc','73108c3a-e190-5c3f-afc8-6df41dc448f4') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('6c2c27c3-210a-5ca4-b322-09aad446f639','3c4f624b-a12f-5b4b-846e-e059ee90b8f3',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/6c2c27c3-210a-5ca4-b322-09aad446f639.jpg','image/jpeg',209288,'a3fb86a403dcd5115dca583bd3e9858035b68955e445588a91c7fe9c7fe864af',1200,1520,TIMESTAMPTZ '2100-01-01 00:00:00+00','블런트 보브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='6c2c27c3-210a-5ca4-b322-09aad446f639' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/6c2c27c3-210a-5ca4-b322-09aad446f639.jpg' AND sha256='a3fb86a403dcd5115dca583bd3e9858035b68955e445588a91c7fe9c7fe864af' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('e0ef9b77-ea49-5d67-80da-673fc665e394','블런트 보브','BOB','6c2c27c3-210a-5ca4-b322-09aad446f639',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('e0ef9b77-ea49-5d67-80da-673fc665e394','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('e0ef9b77-ea49-5d67-80da-673fc665e394','30000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('e0ef9b77-ea49-5d67-80da-673fc665e394','30000000-0000-4000-8000-000000000002') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('b9712588-5c6d-5aeb-b826-3db036524a04','94b77999-44b8-58dd-ba91-8c5b81a16b11',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/b9712588-5c6d-5aeb-b826-3db036524a04.jpg','image/jpeg',174509,'4754adc620a9adb8319e6abd26f1a1bd803ecfae9bb13da2f3b329b9c7fea792',1200,1961,TIMESTAMPTZ '2100-01-01 00:00:00+00','시스루뱅 보브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='b9712588-5c6d-5aeb-b826-3db036524a04' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/b9712588-5c6d-5aeb-b826-3db036524a04.jpg' AND sha256='4754adc620a9adb8319e6abd26f1a1bd803ecfae9bb13da2f3b329b9c7fea792' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('ffe39c84-1b70-58f0-979c-b7474c76888a','시스루뱅 보브','BOB','b9712588-5c6d-5aeb-b826-3db036524a04',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('ffe39c84-1b70-58f0-979c-b7474c76888a','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('ffe39c84-1b70-58f0-979c-b7474c76888a','30000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('ffe39c84-1b70-58f0-979c-b7474c76888a','10000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('4333aaa0-02ed-531d-b768-300535e05b0d','8c9e83bb-3dc1-5e1e-9d5d-870c7f4eff78',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/4333aaa0-02ed-531d-b768-300535e05b0d.jpg','image/jpeg',121672,'79caeede28822f77a66d1756b7efba5b8e0a9ff26f148c4cce89c3f2cf981c0a',1200,1500,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드뱅 클래식 보브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='4333aaa0-02ed-531d-b768-300535e05b0d' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/4333aaa0-02ed-531d-b768-300535e05b0d.jpg' AND sha256='79caeede28822f77a66d1756b7efba5b8e0a9ff26f148c4cce89c3f2cf981c0a' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('16c7597c-2c40-51e1-a754-ef4fe35aa2b3','사이드뱅 클래식 보브','BOB','4333aaa0-02ed-531d-b768-300535e05b0d',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('16c7597c-2c40-51e1-a754-ef4fe35aa2b3','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('16c7597c-2c40-51e1-a754-ef4fe35aa2b3','30000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('16c7597c-2c40-51e1-a754-ef4fe35aa2b3','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9','9cf167c2-79cd-5bd6-8e6f-f9b12596e478',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9.jpg','image/jpeg',249953,'22ee56b41a12d65b621d0ee0a00bdc18a5ab039c1594abffcb643aec1d0179dc',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드파트 원랭스 보브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9.jpg' AND sha256='22ee56b41a12d65b621d0ee0a00bdc18a5ab039c1594abffcb643aec1d0179dc' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('c9442c21-485b-5d64-87c3-a97b491e3702','사이드파트 원랭스 보브','BOB','79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('c9442c21-485b-5d64-87c3-a97b491e3702','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('c9442c21-485b-5d64-87c3-a97b491e3702','30000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('61f943fd-f574-5894-8ea6-916149971fae','6c3a844d-1561-523a-8fc5-4594a020b2c8',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/61f943fd-f574-5894-8ea6-916149971fae.jpg','image/jpeg',133130,'c1e880d9eef714180b0313210e75bf39841b2716c1dc658ffefaf1a198d2c080',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','센터파트 슬릭 보브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='61f943fd-f574-5894-8ea6-916149971fae' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/61f943fd-f574-5894-8ea6-916149971fae.jpg' AND sha256='c1e880d9eef714180b0313210e75bf39841b2716c1dc658ffefaf1a198d2c080' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('e01d6760-17b8-5cd4-97de-c604322e304d','센터파트 슬릭 보브','BOB','61f943fd-f574-5894-8ea6-916149971fae',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('e01d6760-17b8-5cd4-97de-c604322e304d','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',5,'LOW','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('e01d6760-17b8-5cd4-97de-c604322e304d','30000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('e01d6760-17b8-5cd4-97de-c604322e304d','30000000-0000-4000-8000-000000000002') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('917bcbd1-29cb-514c-95ab-1f6240f36631','3a5550dd-50a0-5f36-a02f-f69c8aa75b3f',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/917bcbd1-29cb-514c-95ab-1f6240f36631.jpg','image/jpeg',196822,'372cac673947612bda42e1ccba01e005437c09532b188abffb1869e0d27067c2',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','커튼 레이어드 블로아웃.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='917bcbd1-29cb-514c-95ab-1f6240f36631' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/917bcbd1-29cb-514c-95ab-1f6240f36631.jpg' AND sha256='372cac673947612bda42e1ccba01e005437c09532b188abffb1869e0d27067c2' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('abcb8ccb-9044-5c1b-954e-2c222fc5a701','커튼 레이어드 블로아웃','LONG','917bcbd1-29cb-514c-95ab-1f6240f36631',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('abcb8ccb-9044-5c1b-954e-2c222fc5a701','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('abcb8ccb-9044-5c1b-954e-2c222fc5a701','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('abcb8ccb-9044-5c1b-954e-2c222fc5a701','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('6c25c799-986b-5296-a5fb-2414409f7353','3e6d548d-9ea9-5c71-9184-6c2db305744c',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/6c25c799-986b-5296-a5fb-2414409f7353.jpg','image/jpeg',277049,'05e8acc2e2a72b61379abcd9304f2eb06a4cfe2260ba2ad801a2a663f6822c49',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','하이라이트 롱 S컬.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='6c25c799-986b-5296-a5fb-2414409f7353' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/6c25c799-986b-5296-a5fb-2414409f7353.jpg' AND sha256='05e8acc2e2a72b61379abcd9304f2eb06a4cfe2260ba2ad801a2a663f6822c49' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('fb32cea4-c14f-5c7e-a0d0-2225caf4361c','하이라이트 롱 S컬','LONG','6c25c799-986b-5296-a5fb-2414409f7353',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('fb32cea4-c14f-5c7e-a0d0-2225caf4361c','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('fb32cea4-c14f-5c7e-a0d0-2225caf4361c','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2c517e2b-986e-5bde-8744-9ade9b5f9a7d','웨이브','LONG') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('fb32cea4-c14f-5c7e-a0d0-2225caf4361c','2c517e2b-986e-5bde-8744-9ade9b5f9a7d') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('144afb9a-a898-5f3d-adc1-33b43c08b879','7448211d-037e-5794-9f4b-5b70c2d81f8e',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/144afb9a-a898-5f3d-adc1-33b43c08b879.jpg','image/jpeg',192811,'1811475fed1dffb1f1e8dbf3fdd9938de9e548c9facb36e1f6d39a8aa7b697a9',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','롱 레이어드 C컬.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='144afb9a-a898-5f3d-adc1-33b43c08b879' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/144afb9a-a898-5f3d-adc1-33b43c08b879.jpg' AND sha256='1811475fed1dffb1f1e8dbf3fdd9938de9e548c9facb36e1f6d39a8aa7b697a9' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('5b2448da-3946-5de8-9e22-a1584575acda','롱 레이어드 C컬','LONG','144afb9a-a898-5f3d-adc1-33b43c08b879',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('5b2448da-3946-5de8-9e22-a1584575acda','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('5b2448da-3946-5de8-9e22-a1584575acda','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('621b4131-8d40-57a6-8c47-2a6d665b476d','803d8165-0bb4-5d47-97cf-233b62cbe55a',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/621b4131-8d40-57a6-8c47-2a6d665b476d.jpg','image/jpeg',189980,'ec0c264b5230985b024326a8ecfc1cfc3fe002a01ac022c4d83abc9c7a260e55',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','롱 페더드 레이어드.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='621b4131-8d40-57a6-8c47-2a6d665b476d' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/621b4131-8d40-57a6-8c47-2a6d665b476d.jpg' AND sha256='ec0c264b5230985b024326a8ecfc1cfc3fe002a01ac022c4d83abc9c7a260e55' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('c0dcc01b-d923-56b0-ba41-3e7f060dac05','롱 페더드 레이어드','LONG','621b4131-8d40-57a6-8c47-2a6d665b476d',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('c0dcc01b-d923-56b0-ba41-3e7f060dac05','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('c0dcc01b-d923-56b0-ba41-3e7f060dac05','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('e51b4c2e-ce5e-5817-baff-1280bd64ccf5','06255242-2663-52ee-8248-ed33424e818f',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/e51b4c2e-ce5e-5817-baff-1280bd64ccf5.jpg','image/jpeg',228745,'1ada903281efa4a7723b76708b25db14e60e4e80de4fe9984c2017a55a404d3a',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','볼륨 레이어드 블로아웃.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='e51b4c2e-ce5e-5817-baff-1280bd64ccf5' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/e51b4c2e-ce5e-5817-baff-1280bd64ccf5.jpg' AND sha256='1ada903281efa4a7723b76708b25db14e60e4e80de4fe9984c2017a55a404d3a' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('cc25d0ff-ec17-5eab-9dc3-95b0fcc14ad0','볼륨 레이어드 블로아웃','MEDIUM','e51b4c2e-ce5e-5817-baff-1280bd64ccf5',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('cc25d0ff-ec17-5eab-9dc3-95b0fcc14ad0','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('cc25d0ff-ec17-5eab-9dc3-95b0fcc14ad0','40000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('670329bd-c450-504d-9598-9fea62599e72','ecc49cc9-b20f-52d5-934f-111ff12e24b2',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/670329bd-c450-504d-9598-9fea62599e72.jpg','image/jpeg',215473,'4f9e457294dafd0582ef81708770f93c3974a6cacdaaf5faa164c8df9e53dead',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드뱅 미디엄 C컬.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='670329bd-c450-504d-9598-9fea62599e72' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/670329bd-c450-504d-9598-9fea62599e72.jpg' AND sha256='4f9e457294dafd0582ef81708770f93c3974a6cacdaaf5faa164c8df9e53dead' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('31717139-b772-5190-95ad-bdad71d6aaed','사이드뱅 미디엄 C컬','MEDIUM','670329bd-c450-504d-9598-9fea62599e72',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('31717139-b772-5190-95ad-bdad71d6aaed','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('31717139-b772-5190-95ad-bdad71d6aaed','40000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('31717139-b772-5190-95ad-bdad71d6aaed','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('56bba053-3ad3-562c-b81f-c46044e26049','bc7e5dca-dc8a-5992-bad0-24f87ad7ee32',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/56bba053-3ad3-562c-b81f-c46044e26049.jpg','image/jpeg',210998,'0ae6ff3c97af5b38607afdb24e3f59688f7c0bb81edbbf8d982b67656249bfd2',1200,1804,TIMESTAMPTZ '2100-01-01 00:00:00+00','롱 캐스케이드 웨이브.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='56bba053-3ad3-562c-b81f-c46044e26049' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/56bba053-3ad3-562c-b81f-c46044e26049.jpg' AND sha256='0ae6ff3c97af5b38607afdb24e3f59688f7c0bb81edbbf8d982b67656249bfd2' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('9dde44be-0010-5f35-880b-35075b1c9ca4','롱 캐스케이드 웨이브','LONG','56bba053-3ad3-562c-b81f-c46044e26049',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('9dde44be-0010-5f35-880b-35075b1c9ca4','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('9dde44be-0010-5f35-880b-35075b1c9ca4','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('2c517e2b-986e-5bde-8744-9ade9b5f9a7d','웨이브','LONG') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('9dde44be-0010-5f35-880b-35075b1c9ca4','2c517e2b-986e-5bde-8744-9ade9b5f9a7d') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('5657847a-f497-5fd1-8ca5-7bcb0353e152','695307d0-7ace-5d07-a337-6f68f09ed9bd',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/5657847a-f497-5fd1-8ca5-7bcb0353e152.jpg','image/jpeg',295705,'44f24d6b508713f08c7f9c52f34da78d807467dc44422d47cdbbe768c2e25ee7',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','사이드뱅 페더드컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='5657847a-f497-5fd1-8ca5-7bcb0353e152' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/5657847a-f497-5fd1-8ca5-7bcb0353e152.jpg' AND sha256='44f24d6b508713f08c7f9c52f34da78d807467dc44422d47cdbbe768c2e25ee7' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('5aa1cd83-3951-5ae7-999f-ddd765f75108','사이드뱅 페더드컷','LONG','5657847a-f497-5fd1-8ca5-7bcb0353e152',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('5aa1cd83-3951-5ae7-999f-ddd765f75108','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',15,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('5aa1cd83-3951-5ae7-999f-ddd765f75108','50000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('5aa1cd83-3951-5ae7-999f-ddd765f75108','10000000-0000-4000-8000-000000000003') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('3763f094-afc0-5b98-9160-4ef220ddae94','474249ec-b38e-5af8-b4d8-6f3d50fe4db1',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/3763f094-afc0-5b98-9160-4ef220ddae94.jpg','image/jpeg',242682,'f1cc1f414b115d6e9ee4d18724a8fc4d5af265f094887b0a207f368ce07a0107',1200,1800,TIMESTAMPTZ '2100-01-01 00:00:00+00','미디엄 페더드 C컬.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='3763f094-afc0-5b98-9160-4ef220ddae94' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/3763f094-afc0-5b98-9160-4ef220ddae94.jpg' AND sha256='f1cc1f414b115d6e9ee4d18724a8fc4d5af265f094887b0a207f368ce07a0107' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('e4eff9f7-6ee8-5023-95c1-fdbad8a771e7','미디엄 페더드 C컬','MEDIUM','3763f094-afc0-5b98-9160-4ef220ddae94',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('e4eff9f7-6ee8-5023-95c1-fdbad8a771e7','["CUT", "STYLING"]'::jsonb,'["BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_SHOULDER',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('e4eff9f7-6ee8-5023-95c1-fdbad8a771e7','40000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +INSERT INTO files (file_id,upload_id,user_id,owner_type,purpose,status,object_key,content_type,file_size,sha256,width,height,expires_at,file_name) VALUES ('4f08b7f6-72c1-50be-aa2d-a9f31ef73973','204febd9-3b29-5b90-b6c2-2e3dc2fcff46',NULL,'SYSTEM','HAIRSTYLE_THUMBNAIL','READY','HAIRSTYLE_THUMBNAIL/curated-20261016/4f08b7f6-72c1-50be-aa2d-a9f31ef73973.jpg','image/jpeg',56767,'2bca2225c39b9b46ff61db5584a9cbbd4ba86ca815443eedb65ce01de7d9fc51',1200,675,TIMESTAMPTZ '2100-01-01 00:00:00+00','숏 샤기 멀릿컷.jpg') ON CONFLICT (file_id) DO NOTHING; + +DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM files WHERE file_id='4f08b7f6-72c1-50be-aa2d-a9f31ef73973' AND owner_type='SYSTEM' AND object_key='HAIRSTYLE_THUMBNAIL/curated-20261016/4f08b7f6-72c1-50be-aa2d-a9f31ef73973.jpg' AND sha256='2bca2225c39b9b46ff61db5584a9cbbd4ba86ca815443eedb65ce01de7d9fc51' AND status='READY') THEN RAISE EXCEPTION 'File identity conflict'; END IF; END $$; + +INSERT INTO hairstyle_assets (hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES ('2a7afeef-18a9-5e09-9516-a248e68b4090','숏 샤기 멀릿컷','MEDIUM','4f08b7f6-72c1-50be-aa2d-a9f31ef73973',true,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_recommendation_profiles (hairstyle_id,service_types,compatible_hair_lengths,compatible_hair_types,compatible_hair_thicknesses,compatible_hair_conditions,contraindicated_hair_conditions,minimum_hair_length,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,editorial_priority,metadata_version) VALUES ('2a7afeef-18a9-5e09-9516-a248e68b4090','["CUT", "STYLING"]'::jsonb,'["BELOW_CHIN", "BELOW_SHOULDER", "BELOW_CHEST"]'::jsonb,'["STRAIGHT", "WAVY"]'::jsonb,'["THIN", "NORMAL", "THICK"]'::jsonb,'["HEALTHY", "NORMAL", "DAMAGED", "SEVERELY_DAMAGED"]'::jsonb,'[]'::jsonb,'BELOW_CHIN',10,'MEDIUM','LOW',50,'curated-20261016') ON CONFLICT (hairstyle_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('2a7afeef-18a9-5e09-9516-a248e68b4090','40000000-0000-4000-8000-000000000004') ON CONFLICT DO NOTHING; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('3e2e412d-ee7a-5e5e-b9ae-ad080ca70269','멀릿컷','MEDIUM') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('2a7afeef-18a9-5e09-9516-a248e68b4090','3e2e412d-ee7a-5e5e-b9ae-ad080ca70269') ON CONFLICT DO NOTHING; + +DELETE FROM hairstyle_style_tags WHERE hairstyle_id='71000000-0000-4000-8000-000000000066'; + +INSERT INTO style_tags (style_tag_id,tag_name,category) VALUES ('38808b16-4f6b-563b-981c-90ccc32609a1','콘로우','UPDO') ON CONFLICT (style_tag_id) DO NOTHING; + +INSERT INTO hairstyle_style_tags (hairstyle_id,style_tag_id) VALUES ('71000000-0000-4000-8000-000000000066','38808b16-4f6b-563b-981c-90ccc32609a1'),('71000000-0000-4000-8000-000000000066','60000000-0000-4000-8000-000000000001') ON CONFLICT DO NOTHING; + +UPDATE hairstyle_assets SET active=false,updated_at=now() WHERE hairstyle_id IN ('71000000-0000-4000-8000-000000000068'::uuid,'71000000-0000-4000-8000-000000000075'::uuid,'71000000-0000-4000-8000-000000000076'::uuid,'71000000-0000-4000-8000-000000000077'::uuid,'71000000-0000-4000-8000-000000000078'::uuid,'71000000-0000-4000-8000-000000000001'::uuid,'71000000-0000-4000-8000-000000000023'::uuid,'71000000-0000-4000-8000-000000000027'::uuid,'71000000-0000-4000-8000-000000000028'::uuid,'71000000-0000-4000-8000-000000000030'::uuid,'71000000-0000-4000-8000-000000000050'::uuid,'71000000-0000-4000-8000-000000000051'::uuid,'71000000-0000-4000-8000-000000000054'::uuid,'71000000-0000-4000-8000-000000000055'::uuid,'71000000-0000-4000-8000-000000000056'::uuid,'71000000-0000-4000-8000-000000000057'::uuid,'71000000-0000-4000-8000-000000000058'::uuid,'71000000-0000-4000-8000-000000000059'::uuid,'71000000-0000-4000-8000-000000000060'::uuid,'71000000-0000-4000-8000-000000000061'::uuid,'71000000-0000-4000-8000-000000000062'::uuid,'71000000-0000-4000-8000-000000000063'::uuid,'71000000-0000-4000-8000-000000000064'::uuid,'71000000-0000-4000-8000-000000000065'::uuid,'71000000-0000-4000-8000-000000000067'::uuid,'71000000-0000-4000-8000-000000000033'::uuid,'71000000-0000-4000-8000-000000000039'::uuid,'71000000-0000-4000-8000-000000000042'::uuid,'71000000-0000-4000-8000-000000000043'::uuid,'71000000-0000-4000-8000-000000000046'::uuid,'71000000-0000-4000-8000-000000000073'::uuid,'71000000-0000-4000-8000-000000000080'::uuid,'71000000-0000-4000-8000-000000000081'::uuid,'71000000-0000-4000-8000-000000000082'::uuid,'71000000-0000-4000-8000-000000000084'::uuid,'71000000-0000-4000-8000-000000000085'::uuid,'71000000-0000-4000-8000-000000000088'::uuid,'71000000-0000-4000-8000-000000000089'::uuid,'71000000-0000-4000-8000-000000000090'::uuid,'71000000-0000-4000-8000-000000000093'::uuid,'71000000-0000-4000-8000-000000000094'::uuid,'71000000-0000-4000-8000-000000000095'::uuid,'71000000-0000-4000-8000-000000000097'::uuid,'71000000-0000-4000-8000-000000000100'::uuid,'71000000-0000-4000-8000-000000000004'::uuid,'71000000-0000-4000-8000-000000000006'::uuid,'71000000-0000-4000-8000-000000000035'::uuid,'71000000-0000-4000-8000-000000000036'::uuid,'71000000-0000-4000-8000-000000000049'::uuid,'71000000-0000-4000-8000-000000000070'::uuid,'71000000-0000-4000-8000-000000000072'::uuid,'71000000-0000-4000-8000-000000000079'::uuid,'71000000-0000-4000-8000-000000000091'::uuid,'71000000-0000-4000-8000-000000000098'::uuid,'71000000-0000-4000-8000-000000000099'::uuid,'71000000-0000-4000-8000-000000000005'::uuid,'71000000-0000-4000-8000-000000000034'::uuid,'71000000-0000-4000-8000-000000000041'::uuid) AND active; + +UPDATE hairstyle_assets SET style_name='콘로우 로우번',category='BRAIDED',asset_version='curated-20261016',updated_at=now() WHERE hairstyle_id='71000000-0000-4000-8000-000000000066' AND asset_version <> 'curated-20261016'; + +UPDATE hairstyle_recommendation_profiles SET service_types='["STYLING"]'::jsonb,minimum_hair_length='BELOW_SHOULDER',compatible_hair_lengths='["BELOW_SHOULDER","BELOW_CHEST"]'::jsonb,metadata_version='curated-20261016',updated_at=now() WHERE hairstyle_id='71000000-0000-4000-8000-000000000066' AND metadata_version <> 'curated-20261016'; + +DO $$ BEGIN IF (SELECT count(*) FROM hairstyle_assets a JOIN files f ON f.file_id=a.thumbnail_file_id JOIN hairstyle_recommendation_profiles p USING(hairstyle_id) WHERE a.hairstyle_id IN ('0d198408-5bf7-514c-b3af-52d56d990361'::uuid,'81871f24-b393-593a-b0b8-f31d7296118a'::uuid,'7ffbc247-3416-5316-905e-6fe769a4153f'::uuid,'12d8e1fd-b70d-50e0-a0d5-a4d769759969'::uuid,'becfa223-4c43-5ca1-80b0-1e3cd17bf008'::uuid,'2f367c11-435d-5c1f-b005-45af3c693de2'::uuid,'a04671ae-c01b-5568-8897-cea144960c1d'::uuid,'3ae493ce-1c5c-566c-95a2-ca69ace18f5f'::uuid,'0c29ffac-9ee8-56b8-a7bf-13d46a0830a5'::uuid,'3719ea70-8240-5652-8a16-02f8f5685b5f'::uuid,'55a650e5-5a99-5ed7-bd78-2ede7a818ed6'::uuid,'f44a00c9-3c3f-5927-98f7-32966c2b137e'::uuid,'090392ec-2fcb-56e3-ba4c-0063af2471d3'::uuid,'30482fbb-adf7-5843-a988-f340b85fa429'::uuid,'cb9713ac-a1b0-5e95-a303-e24bc4a425ac'::uuid,'7d3fb671-6ae0-5bcb-a9f7-54d7c5f534f9'::uuid,'b98199c9-df22-5914-8d6c-ff1c9818aa2e'::uuid,'7f17a8fe-2b82-558e-969f-79a3563ce8fb'::uuid,'87408dfc-91cf-53df-bfab-369e62e93e38'::uuid,'b4b3e4fc-cd3e-5df2-ae97-4947e1076293'::uuid,'b252732a-2d91-5c01-afd5-6d36f1bd3a92'::uuid,'86c65822-8ea7-58c4-b115-d9924435ea68'::uuid,'8a9d43c6-cf38-5cc3-b038-94fe0f689de7'::uuid,'c17d60af-6144-5d44-9658-eda2e5ad638d'::uuid,'06cc4930-e540-5d6f-b284-1e08b1818a1c'::uuid,'76145924-61ee-52ca-8fb6-faf5392da1d5'::uuid,'447ef348-7a08-5565-b345-de7ba9aa77fd'::uuid,'cbf47ccd-6a33-5cc1-963d-6dcd61fd0189'::uuid,'02310a32-527e-599b-aafd-a64c63398d68'::uuid,'4972ab0a-e635-58d1-85e3-9889c6cce4a7'::uuid,'42d2fb31-6285-5aa4-864f-8fbe10e75a5c'::uuid,'f055858a-07fe-5a79-a257-b7ccc3dab7f8'::uuid,'8167d07c-eb7e-5632-878a-0df3e0ac5ee7'::uuid,'6abf9427-67ac-5754-a2f3-a29c50a8f4b6'::uuid,'b1fdcdba-f935-5f0d-8fba-e9b56f3003fc'::uuid,'e0ef9b77-ea49-5d67-80da-673fc665e394'::uuid,'ffe39c84-1b70-58f0-979c-b7474c76888a'::uuid,'16c7597c-2c40-51e1-a754-ef4fe35aa2b3'::uuid,'c9442c21-485b-5d64-87c3-a97b491e3702'::uuid,'e01d6760-17b8-5cd4-97de-c604322e304d'::uuid,'abcb8ccb-9044-5c1b-954e-2c222fc5a701'::uuid,'fb32cea4-c14f-5c7e-a0d0-2225caf4361c'::uuid,'5b2448da-3946-5de8-9e22-a1584575acda'::uuid,'c0dcc01b-d923-56b0-ba41-3e7f060dac05'::uuid,'cc25d0ff-ec17-5eab-9dc3-95b0fcc14ad0'::uuid,'31717139-b772-5190-95ad-bdad71d6aaed'::uuid,'9dde44be-0010-5f35-880b-35075b1c9ca4'::uuid,'5aa1cd83-3951-5ae7-999f-ddd765f75108'::uuid,'e4eff9f7-6ee8-5023-95c1-fdbad8a771e7'::uuid,'2a7afeef-18a9-5e09-9516-a248e68b4090'::uuid) AND a.active AND a.asset_version='curated-20261016' AND f.status='READY' AND p.minimum_hair_length IS NOT NULL) <> 50 THEN RAISE EXCEPTION 'Expected 50 ready replacement styles'; END IF; IF EXISTS (SELECT 1 FROM hairstyle_assets WHERE hairstyle_id IN ('71000000-0000-4000-8000-000000000068'::uuid,'71000000-0000-4000-8000-000000000075'::uuid,'71000000-0000-4000-8000-000000000076'::uuid,'71000000-0000-4000-8000-000000000077'::uuid,'71000000-0000-4000-8000-000000000078'::uuid,'71000000-0000-4000-8000-000000000001'::uuid,'71000000-0000-4000-8000-000000000023'::uuid,'71000000-0000-4000-8000-000000000027'::uuid,'71000000-0000-4000-8000-000000000028'::uuid,'71000000-0000-4000-8000-000000000030'::uuid,'71000000-0000-4000-8000-000000000050'::uuid,'71000000-0000-4000-8000-000000000051'::uuid,'71000000-0000-4000-8000-000000000054'::uuid,'71000000-0000-4000-8000-000000000055'::uuid,'71000000-0000-4000-8000-000000000056'::uuid,'71000000-0000-4000-8000-000000000057'::uuid,'71000000-0000-4000-8000-000000000058'::uuid,'71000000-0000-4000-8000-000000000059'::uuid,'71000000-0000-4000-8000-000000000060'::uuid,'71000000-0000-4000-8000-000000000061'::uuid,'71000000-0000-4000-8000-000000000062'::uuid,'71000000-0000-4000-8000-000000000063'::uuid,'71000000-0000-4000-8000-000000000064'::uuid,'71000000-0000-4000-8000-000000000065'::uuid,'71000000-0000-4000-8000-000000000067'::uuid,'71000000-0000-4000-8000-000000000033'::uuid,'71000000-0000-4000-8000-000000000039'::uuid,'71000000-0000-4000-8000-000000000042'::uuid,'71000000-0000-4000-8000-000000000043'::uuid,'71000000-0000-4000-8000-000000000046'::uuid,'71000000-0000-4000-8000-000000000073'::uuid,'71000000-0000-4000-8000-000000000080'::uuid,'71000000-0000-4000-8000-000000000081'::uuid,'71000000-0000-4000-8000-000000000082'::uuid,'71000000-0000-4000-8000-000000000084'::uuid,'71000000-0000-4000-8000-000000000085'::uuid,'71000000-0000-4000-8000-000000000088'::uuid,'71000000-0000-4000-8000-000000000089'::uuid,'71000000-0000-4000-8000-000000000090'::uuid,'71000000-0000-4000-8000-000000000093'::uuid,'71000000-0000-4000-8000-000000000094'::uuid,'71000000-0000-4000-8000-000000000095'::uuid,'71000000-0000-4000-8000-000000000097'::uuid,'71000000-0000-4000-8000-000000000100'::uuid,'71000000-0000-4000-8000-000000000004'::uuid,'71000000-0000-4000-8000-000000000006'::uuid,'71000000-0000-4000-8000-000000000035'::uuid,'71000000-0000-4000-8000-000000000036'::uuid,'71000000-0000-4000-8000-000000000049'::uuid,'71000000-0000-4000-8000-000000000070'::uuid,'71000000-0000-4000-8000-000000000072'::uuid,'71000000-0000-4000-8000-000000000079'::uuid,'71000000-0000-4000-8000-000000000091'::uuid,'71000000-0000-4000-8000-000000000098'::uuid,'71000000-0000-4000-8000-000000000099'::uuid,'71000000-0000-4000-8000-000000000005'::uuid,'71000000-0000-4000-8000-000000000034'::uuid,'71000000-0000-4000-8000-000000000041'::uuid) AND active) THEN RAISE EXCEPTION 'Retired styles are still active'; END IF; END $$; + +COMMIT; diff --git a/catalog/curated-20261016/manifest.json b/catalog/curated-20261016/manifest.json new file mode 100644 index 00000000..3ea2f1f5 --- /dev/null +++ b/catalog/curated-20261016/manifest.json @@ -0,0 +1,3963 @@ +{ + "release": "curated-20261016", + "notes": [ + "Source pages show Free under the Pexels License; website/app use reviewed. Independent model releases are not available or verified.", + "Style presentation describes catalogue curation, not the identity of the depicted person, and is not used to infer user gender.", + "Names and length are visual editorial classifications. Care time, difficulty and compatibility are provisional editorial estimates, not hairdresser-verified.", + "CUT/STYLING describe this non-chemical recommendation path; actual perm/dye procedures cannot be established from a photograph.", + "Only active catalogue entries are retired. Historical recommendations, saved styles and old S3 objects remain intact." + ], + "replacement_numbers": [ + 2, + 5, + 6, + 7, + 8, + 9, + 23, + 27, + 28, + 30, + 31, + 32, + 35, + 36, + 37, + 38, + 39, + 56, + 59, + 61, + 62, + 64, + 67, + 68, + 69, + 70, + 72, + 73, + 76, + 77, + 78, + 80, + 81, + 82, + 84, + 85, + 86, + 87, + 88, + 89, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100 + ], + "duplicate_numbers": [ + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 48 + ], + "representative_number": 47, + "original": [ + { + "number": 1, + "hairstyle_id": "71000000-0000-4000-8000-000000000045", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000045", + "style_name": "숏 스타일 01", + "category": "SHORT", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000045.jpg" + }, + { + "number": 2, + "hairstyle_id": "71000000-0000-4000-8000-000000000068", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000068", + "style_name": "숏 스타일 02", + "category": "SHORT", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000068.jpg" + }, + { + "number": 3, + "hairstyle_id": "71000000-0000-4000-8000-000000000069", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000069", + "style_name": "숏 스타일 03", + "category": "SHORT", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000069.jpg" + }, + { + "number": 4, + "hairstyle_id": "71000000-0000-4000-8000-000000000074", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000074", + "style_name": "숏 스타일 04", + "category": "SHORT", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000074.jpg" + }, + { + "number": 5, + "hairstyle_id": "71000000-0000-4000-8000-000000000075", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000075", + "style_name": "숏 스타일 05", + "category": "SHORT", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000075.jpg" + }, + { + "number": 6, + "hairstyle_id": "71000000-0000-4000-8000-000000000076", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000076", + "style_name": "숏 스타일 06", + "category": "SHORT", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000076.jpg" + }, + { + "number": 7, + "hairstyle_id": "71000000-0000-4000-8000-000000000077", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000077", + "style_name": "숏 스타일 07", + "category": "SHORT", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000077.jpg" + }, + { + "number": 8, + "hairstyle_id": "71000000-0000-4000-8000-000000000078", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000078", + "style_name": "숏 스타일 08", + "category": "SHORT", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000078.jpg" + }, + { + "number": 9, + "hairstyle_id": "71000000-0000-4000-8000-000000000001", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000001", + "style_name": "클린 픽시컷", + "category": "SHORT", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000001.jpg" + }, + { + "number": 10, + "hairstyle_id": "71000000-0000-4000-8000-000000000002", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000002", + "style_name": "클래식 보브", + "category": "BOB", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000002.jpg" + }, + { + "number": 11, + "hairstyle_id": "71000000-0000-4000-8000-000000000003", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000003", + "style_name": "미디엄 레이어드 웨이브", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000003.jpg" + }, + { + "number": 12, + "hairstyle_id": "71000000-0000-4000-8000-000000000007", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000007", + "style_name": "미디엄 스타일 01", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000007.jpg" + }, + { + "number": 13, + "hairstyle_id": "71000000-0000-4000-8000-000000000008", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000008", + "style_name": "미디엄 스타일 02", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000008.jpg" + }, + { + "number": 14, + "hairstyle_id": "71000000-0000-4000-8000-000000000009", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000009", + "style_name": "미디엄 스타일 03", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000009.jpg" + }, + { + "number": 15, + "hairstyle_id": "71000000-0000-4000-8000-000000000010", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000010", + "style_name": "미디엄 스타일 04", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000010.jpg" + }, + { + "number": 16, + "hairstyle_id": "71000000-0000-4000-8000-000000000011", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000011", + "style_name": "미디엄 스타일 05", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000011.jpg" + }, + { + "number": 17, + "hairstyle_id": "71000000-0000-4000-8000-000000000014", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000014", + "style_name": "미디엄 스타일 06", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000014.jpg" + }, + { + "number": 18, + "hairstyle_id": "71000000-0000-4000-8000-000000000015", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000015", + "style_name": "미디엄 스타일 07", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000015.jpg" + }, + { + "number": 19, + "hairstyle_id": "71000000-0000-4000-8000-000000000016", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000016", + "style_name": "미디엄 스타일 08", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000016.jpg" + }, + { + "number": 20, + "hairstyle_id": "71000000-0000-4000-8000-000000000017", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000017", + "style_name": "미디엄 스타일 09", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000017.jpg" + }, + { + "number": 21, + "hairstyle_id": "71000000-0000-4000-8000-000000000021", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000021", + "style_name": "미디엄 스타일 10", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000021.jpg" + }, + { + "number": 22, + "hairstyle_id": "71000000-0000-4000-8000-000000000022", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000022", + "style_name": "미디엄 스타일 11", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000022.jpg" + }, + { + "number": 23, + "hairstyle_id": "71000000-0000-4000-8000-000000000023", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000023", + "style_name": "미디엄 스타일 12", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000023.jpg" + }, + { + "number": 24, + "hairstyle_id": "71000000-0000-4000-8000-000000000024", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000024", + "style_name": "미디엄 스타일 13", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000024.jpg" + }, + { + "number": 25, + "hairstyle_id": "71000000-0000-4000-8000-000000000025", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000025", + "style_name": "미디엄 스타일 14", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000025.jpg" + }, + { + "number": 26, + "hairstyle_id": "71000000-0000-4000-8000-000000000026", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000026", + "style_name": "미디엄 스타일 15", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000026.jpg" + }, + { + "number": 27, + "hairstyle_id": "71000000-0000-4000-8000-000000000027", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000027", + "style_name": "미디엄 스타일 16", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000027.jpg" + }, + { + "number": 28, + "hairstyle_id": "71000000-0000-4000-8000-000000000028", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000028", + "style_name": "미디엄 스타일 17", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000028.jpg" + }, + { + "number": 29, + "hairstyle_id": "71000000-0000-4000-8000-000000000029", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000029", + "style_name": "미디엄 스타일 18", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000029.jpg" + }, + { + "number": 30, + "hairstyle_id": "71000000-0000-4000-8000-000000000030", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000030", + "style_name": "미디엄 스타일 19", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000030.jpg" + }, + { + "number": 31, + "hairstyle_id": "71000000-0000-4000-8000-000000000050", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000050", + "style_name": "미디엄 스타일 20", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000050.jpg" + }, + { + "number": 32, + "hairstyle_id": "71000000-0000-4000-8000-000000000051", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000051", + "style_name": "미디엄 스타일 21", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000051.jpg" + }, + { + "number": 33, + "hairstyle_id": "71000000-0000-4000-8000-000000000052", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000052", + "style_name": "미디엄 스타일 22", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000052.jpg" + }, + { + "number": 34, + "hairstyle_id": "71000000-0000-4000-8000-000000000053", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000053", + "style_name": "미디엄 스타일 23", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000053.jpg" + }, + { + "number": 35, + "hairstyle_id": "71000000-0000-4000-8000-000000000054", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000054", + "style_name": "미디엄 스타일 24", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000054.jpg" + }, + { + "number": 36, + "hairstyle_id": "71000000-0000-4000-8000-000000000055", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000055", + "style_name": "미디엄 스타일 25", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000055.jpg" + }, + { + "number": 37, + "hairstyle_id": "71000000-0000-4000-8000-000000000056", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000056", + "style_name": "미디엄 스타일 26", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000056.jpg" + }, + { + "number": 38, + "hairstyle_id": "71000000-0000-4000-8000-000000000057", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000057", + "style_name": "미디엄 스타일 27", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000057.jpg" + }, + { + "number": 39, + "hairstyle_id": "71000000-0000-4000-8000-000000000058", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000058", + "style_name": "미디엄 스타일 28", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000058.jpg" + }, + { + "number": 40, + "hairstyle_id": "71000000-0000-4000-8000-000000000059", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000059", + "style_name": "미디엄 스타일 29", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000059.jpg" + }, + { + "number": 41, + "hairstyle_id": "71000000-0000-4000-8000-000000000060", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000060", + "style_name": "미디엄 스타일 30", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000060.jpg" + }, + { + "number": 42, + "hairstyle_id": "71000000-0000-4000-8000-000000000061", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000061", + "style_name": "미디엄 스타일 31", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000061.jpg" + }, + { + "number": 43, + "hairstyle_id": "71000000-0000-4000-8000-000000000062", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000062", + "style_name": "미디엄 스타일 32", + "category": "MEDIUM", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000062.jpg" + }, + { + "number": 44, + "hairstyle_id": "71000000-0000-4000-8000-000000000063", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000063", + "style_name": "미디엄 스타일 33", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000063.jpg" + }, + { + "number": 45, + "hairstyle_id": "71000000-0000-4000-8000-000000000064", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000064", + "style_name": "미디엄 스타일 34", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000064.jpg" + }, + { + "number": 46, + "hairstyle_id": "71000000-0000-4000-8000-000000000065", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000065", + "style_name": "미디엄 스타일 35", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000065.jpg" + }, + { + "number": 47, + "hairstyle_id": "71000000-0000-4000-8000-000000000066", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000066", + "style_name": "미디엄 스타일 36", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000066.jpg" + }, + { + "number": 48, + "hairstyle_id": "71000000-0000-4000-8000-000000000067", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000067", + "style_name": "미디엄 스타일 37", + "category": "MEDIUM", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000067.jpg" + }, + { + "number": 49, + "hairstyle_id": "71000000-0000-4000-8000-000000000012", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000012", + "style_name": "롱 스타일 01", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000012.jpg" + }, + { + "number": 50, + "hairstyle_id": "71000000-0000-4000-8000-000000000013", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000013", + "style_name": "롱 스타일 02", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000013.jpg" + }, + { + "number": 51, + "hairstyle_id": "71000000-0000-4000-8000-000000000018", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000018", + "style_name": "롱 스타일 03", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000018.jpg" + }, + { + "number": 52, + "hairstyle_id": "71000000-0000-4000-8000-000000000019", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000019", + "style_name": "롱 스타일 04", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000019.jpg" + }, + { + "number": 53, + "hairstyle_id": "71000000-0000-4000-8000-000000000020", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000020", + "style_name": "롱 스타일 05", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000020.jpg" + }, + { + "number": 54, + "hairstyle_id": "71000000-0000-4000-8000-000000000031", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000031", + "style_name": "롱 스타일 06", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000031.jpg" + }, + { + "number": 55, + "hairstyle_id": "71000000-0000-4000-8000-000000000032", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000032", + "style_name": "롱 스타일 07", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000032.jpg" + }, + { + "number": 56, + "hairstyle_id": "71000000-0000-4000-8000-000000000033", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000033", + "style_name": "롱 스타일 08", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000033.jpg" + }, + { + "number": 57, + "hairstyle_id": "71000000-0000-4000-8000-000000000037", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000037", + "style_name": "롱 스타일 09", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000037.jpg" + }, + { + "number": 58, + "hairstyle_id": "71000000-0000-4000-8000-000000000038", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000038", + "style_name": "롱 스타일 10", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000038.jpg" + }, + { + "number": 59, + "hairstyle_id": "71000000-0000-4000-8000-000000000039", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000039", + "style_name": "롱 스타일 11", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000039.jpg" + }, + { + "number": 60, + "hairstyle_id": "71000000-0000-4000-8000-000000000040", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000040", + "style_name": "롱 스타일 12", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000040.jpg" + }, + { + "number": 61, + "hairstyle_id": "71000000-0000-4000-8000-000000000042", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000042", + "style_name": "롱 스타일 13", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000042.jpg" + }, + { + "number": 62, + "hairstyle_id": "71000000-0000-4000-8000-000000000043", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000043", + "style_name": "롱 스타일 14", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000043.jpg" + }, + { + "number": 63, + "hairstyle_id": "71000000-0000-4000-8000-000000000044", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000044", + "style_name": "롱 스타일 15", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000044.jpg" + }, + { + "number": 64, + "hairstyle_id": "71000000-0000-4000-8000-000000000046", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000046", + "style_name": "롱 스타일 16", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000046.jpg" + }, + { + "number": 65, + "hairstyle_id": "71000000-0000-4000-8000-000000000048", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000048", + "style_name": "롱 스타일 17", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000048.jpg" + }, + { + "number": 66, + "hairstyle_id": "71000000-0000-4000-8000-000000000071", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000071", + "style_name": "롱 스타일 18", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000071.jpg" + }, + { + "number": 67, + "hairstyle_id": "71000000-0000-4000-8000-000000000073", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000073", + "style_name": "롱 스타일 19", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000073.jpg" + }, + { + "number": 68, + "hairstyle_id": "71000000-0000-4000-8000-000000000080", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000080", + "style_name": "롱 스타일 20", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000080.jpg" + }, + { + "number": 69, + "hairstyle_id": "71000000-0000-4000-8000-000000000081", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000081", + "style_name": "롱 스타일 21", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000081.jpg" + }, + { + "number": 70, + "hairstyle_id": "71000000-0000-4000-8000-000000000082", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000082", + "style_name": "롱 스타일 22", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000082.jpg" + }, + { + "number": 71, + "hairstyle_id": "71000000-0000-4000-8000-000000000083", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000083", + "style_name": "롱 스타일 23", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000083.jpg" + }, + { + "number": 72, + "hairstyle_id": "71000000-0000-4000-8000-000000000084", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000084", + "style_name": "롱 스타일 24", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000084.jpg" + }, + { + "number": 73, + "hairstyle_id": "71000000-0000-4000-8000-000000000085", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000085", + "style_name": "롱 스타일 25", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000085.jpg" + }, + { + "number": 74, + "hairstyle_id": "71000000-0000-4000-8000-000000000086", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000086", + "style_name": "롱 스타일 26", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000086.jpg" + }, + { + "number": 75, + "hairstyle_id": "71000000-0000-4000-8000-000000000087", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000087", + "style_name": "롱 스타일 27", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000087.jpg" + }, + { + "number": 76, + "hairstyle_id": "71000000-0000-4000-8000-000000000088", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000088", + "style_name": "롱 스타일 28", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000088.jpg" + }, + { + "number": 77, + "hairstyle_id": "71000000-0000-4000-8000-000000000089", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000089", + "style_name": "롱 스타일 29", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000089.jpg" + }, + { + "number": 78, + "hairstyle_id": "71000000-0000-4000-8000-000000000090", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000090", + "style_name": "롱 스타일 30", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000090.jpg" + }, + { + "number": 79, + "hairstyle_id": "71000000-0000-4000-8000-000000000092", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000092", + "style_name": "롱 스타일 31", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000092.jpg" + }, + { + "number": 80, + "hairstyle_id": "71000000-0000-4000-8000-000000000093", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000093", + "style_name": "롱 스타일 32", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000093.jpg" + }, + { + "number": 81, + "hairstyle_id": "71000000-0000-4000-8000-000000000094", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000094", + "style_name": "롱 스타일 33", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000094.jpg" + }, + { + "number": 82, + "hairstyle_id": "71000000-0000-4000-8000-000000000095", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000095", + "style_name": "롱 스타일 34", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000095.jpg" + }, + { + "number": 83, + "hairstyle_id": "71000000-0000-4000-8000-000000000096", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000096", + "style_name": "롱 스타일 35", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000096.jpg" + }, + { + "number": 84, + "hairstyle_id": "71000000-0000-4000-8000-000000000097", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000097", + "style_name": "롱 스타일 36", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000097.jpg" + }, + { + "number": 85, + "hairstyle_id": "71000000-0000-4000-8000-000000000100", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000100", + "style_name": "롱 스타일 37", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000100.jpg" + }, + { + "number": 86, + "hairstyle_id": "71000000-0000-4000-8000-000000000004", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000004", + "style_name": "롱 스트레이트", + "category": "LONG", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000004.jpg" + }, + { + "number": 87, + "hairstyle_id": "71000000-0000-4000-8000-000000000006", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000006", + "style_name": "컬리 볼륨", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000006.jpg" + }, + { + "number": 88, + "hairstyle_id": "71000000-0000-4000-8000-000000000035", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000035", + "style_name": "컬리 스타일 01", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000035.jpg" + }, + { + "number": 89, + "hairstyle_id": "71000000-0000-4000-8000-000000000036", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000036", + "style_name": "컬리 스타일 02", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000036.jpg" + }, + { + "number": 90, + "hairstyle_id": "71000000-0000-4000-8000-000000000047", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000047", + "style_name": "컬리 스타일 03", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000047.jpg" + }, + { + "number": 91, + "hairstyle_id": "71000000-0000-4000-8000-000000000049", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000049", + "style_name": "컬리 스타일 04", + "category": "CURLY", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000049.jpg" + }, + { + "number": 92, + "hairstyle_id": "71000000-0000-4000-8000-000000000070", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000070", + "style_name": "컬리 스타일 05", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000070.jpg" + }, + { + "number": 93, + "hairstyle_id": "71000000-0000-4000-8000-000000000072", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000072", + "style_name": "컬리 스타일 06", + "category": "CURLY", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000072.jpg" + }, + { + "number": 94, + "hairstyle_id": "71000000-0000-4000-8000-000000000079", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000079", + "style_name": "컬리 스타일 07", + "category": "CURLY", + "active": false, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000079.jpg" + }, + { + "number": 95, + "hairstyle_id": "71000000-0000-4000-8000-000000000091", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000091", + "style_name": "컬리 스타일 08", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000091.jpg" + }, + { + "number": 96, + "hairstyle_id": "71000000-0000-4000-8000-000000000098", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000098", + "style_name": "컬리 스타일 09", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000098.jpg" + }, + { + "number": 97, + "hairstyle_id": "71000000-0000-4000-8000-000000000099", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000099", + "style_name": "컬리 스타일 10", + "category": "CURLY", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000099.jpg" + }, + { + "number": 98, + "hairstyle_id": "71000000-0000-4000-8000-000000000005", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000005", + "style_name": "롱 내추럴 웨이브", + "category": "LONG_WAVE", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000005.jpg" + }, + { + "number": 99, + "hairstyle_id": "71000000-0000-4000-8000-000000000034", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000034", + "style_name": "웨이브 스타일 01", + "category": "WAVE", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000034.jpg" + }, + { + "number": 100, + "hairstyle_id": "71000000-0000-4000-8000-000000000041", + "thumbnail_file_id": "72000000-0000-4000-8000-000000000041", + "style_name": "웨이브 스타일 02", + "category": "WAVE", + "active": true, + "object_key": "HAIRSTYLE_THUMBNAIL/00000000-0000-0000-0000-000000000000/72000000-0000-4000-8000-000000000041.jpg" + } + ], + "entries": [ + { + "replaces_number": 2, + "hairstyle_id": "0d198408-5bf7-514c-b3af-52d56d990361", + "file_id": "daa307bc-d79f-56f6-9ec4-76d55ab60d84", + "upload_id": "eafbe55d-7dab-5cc3-b523-1f616851a0c2", + "style_name": "하드파트 포마드컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/12706272.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/daa307bc-d79f-56f6-9ec4-76d55ab60d84.jpg", + "sha256": "7cd22bb04afe2a027620e55ba246e9bcff19e5e39e930f1c3f8fd5c844a84f5b", + "file_size": 72941, + "width": 1200, + "height": 800, + "source_photo_id": "12706272", + "source_url": "https://www.pexels.com/photo/man-at-barber-shop-12706272/", + "download_url": "https://images.pexels.com/photos/12706272/pexels-photo-12706272.jpeg?auto=compress&w=1200", + "photographer": "iam luisao", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "포마드컷", + "사이드파트" + ] + }, + { + "replaces_number": 5, + "hairstyle_id": "81871f24-b393-593a-b0b8-f31d7296118a", + "file_id": "0d9c8d3c-6b58-57fa-9764-f8967cb516d0", + "upload_id": "1dc5de0a-e214-58a9-85e4-e1c96cf4e934", + "style_name": "라인 언더컷 퀴프", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/16098668.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/0d9c8d3c-6b58-57fa-9764-f8967cb516d0.jpg", + "sha256": "718b928a8a0238fe6af5352ff303cffd0bec05c807f6ec0aefe02502fd9532db", + "file_size": 204609, + "width": 1200, + "height": 1800, + "source_photo_id": "16098668", + "source_url": "https://www.pexels.com/photo/man-dressed-in-a-shirt-at-the-barbershop-16098668/", + "download_url": "https://images.pexels.com/photos/16098668/pexels-photo-16098668/free-photo-of-man-dressed-in-a-shirt-at-the-barbershop.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "언더컷", + "리젠트컷" + ] + }, + { + "replaces_number": 6, + "hairstyle_id": "7ffbc247-3416-5316-905e-6fe769a4153f", + "file_id": "b38860ec-2947-5bab-b2f5-f603b60f4296", + "upload_id": "d33891da-54c0-5dbc-a91b-5fef07b7a52f", + "style_name": "텍스처 크롭 페이드", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/27467943.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/b38860ec-2947-5bab-b2f5-f603b60f4296.jpg", + "sha256": "95aaf9d6b7b885d8187db8cf199f172ef41ad3871db29640a9311b5e56a4aa9a", + "file_size": 291875, + "width": 1200, + "height": 2133, + "source_photo_id": "27467943", + "source_url": "https://www.pexels.com/photo/barber-shop-27467943/", + "download_url": "https://images.pexels.com/photos/27467943/pexels-photo-27467943/free-photo-of-barber-shop.jpeg?auto=compress&w=1200", + "photographer": "Clark Enzzo", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "크롭컷", + "페이드컷" + ] + }, + { + "replaces_number": 7, + "hairstyle_id": "12d8e1fd-b70d-50e0-a0d5-a4d769759969", + "file_id": "b08e5ad3-825b-59b0-80ea-c16864f87666", + "upload_id": "35fea6d2-b11b-5755-8c7a-9f2f9d2e1236", + "style_name": "클래식 폼파두르", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/5337945.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/b08e5ad3-825b-59b0-80ea-c16864f87666.jpg", + "sha256": "cac53507b351588dab19b86bb1337649ac781e17e49f459be060d17c5796fc3c", + "file_size": 66203, + "width": 1200, + "height": 800, + "source_photo_id": "5337945", + "source_url": "https://www.pexels.com/photo/a-man-wearing-a-black-crew-neck-shirt-5337945/", + "download_url": "https://images.pexels.com/photos/5337945/pexels-photo-5337945.jpeg?auto=compress&w=1200", + "photographer": "alexandre saraiva carniato", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "HIGH", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "포마드컷", + "리젠트컷" + ] + }, + { + "replaces_number": 8, + "hairstyle_id": "becfa223-4c43-5ca1-80b0-1e3cd17bf008", + "file_id": "3a4285bc-72f7-5c24-a42b-5f6e8de43de0", + "upload_id": "60cd55cf-6f1a-57c1-a317-f15253bf74d8", + "style_name": "하이 퀴프 페이드", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/7460149.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/3a4285bc-72f7-5c24-a42b-5f6e8de43de0.jpg", + "sha256": "4ae41d430b48f4c3ba0cd012c55662e65b7e1a586d78dd10487f828135595314", + "file_size": 255717, + "width": 1200, + "height": 1832, + "source_photo_id": "7460149", + "source_url": "https://www.pexels.com/photo/photo-of-man-with-facial-hair-and-trendy-hairstyle-7460149/", + "download_url": "https://images.pexels.com/photos/7460149/pexels-photo-7460149.jpeg?auto=compress&w=1200", + "photographer": "Damian Barczak", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "리젠트컷", + "페이드컷" + ] + }, + { + "replaces_number": 9, + "hairstyle_id": "2f367c11-435d-5c1f-b005-45af3c693de2", + "file_id": "9e42ce49-eaed-5b2d-8cea-9e25541ccfcc", + "upload_id": "b34630f0-9ebe-5a7b-adc3-b9621bee9bec", + "style_name": "센터파트 볼륨 가르마", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/16569324.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/9e42ce49-eaed-5b2d-8cea-9e25541ccfcc.jpg", + "sha256": "ad553a0d085329b90b157a80f135bd31074d44b5610474831c3a03d95a6c94d1", + "file_size": 158675, + "width": 1200, + "height": 1600, + "source_photo_id": "16569324", + "source_url": "https://www.pexels.com/photo/close-up-of-young-man-in-black-clothes-16569324/", + "download_url": "https://images.pexels.com/photos/16569324/pexels-photo-16569324/free-photo-of-close-up-of-young-man-in-black-clothes.jpeg?auto=compress&w=1200", + "photographer": "rao qingwei", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "가르마컷", + "사이드뱅" + ] + }, + { + "replaces_number": 23, + "hairstyle_id": "a04671ae-c01b-5568-8897-cea144960c1d", + "file_id": "a4de0c0c-d7db-5176-8b22-5c8a5069f52c", + "upload_id": "7adc82e6-acb0-5d0d-9d88-3132604861c0", + "style_name": "숄더 렝스 웨트 웨이브", + "category": "MEDIUM", + "style_presentation": "MASCULINE", + "image_file": "images/19523960.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/a4de0c0c-d7db-5176-8b22-5c8a5069f52c.jpg", + "sha256": "c4a8614f3ef12d4ef1c3f9fad4cb136456263aed7f01a742345e6839a3d48b5a", + "file_size": 155578, + "width": 1200, + "height": 1600, + "source_photo_id": "19523960", + "source_url": "https://www.pexels.com/photo/black-and-white-portrait-of-a-man-19523960/", + "download_url": "https://images.pexels.com/photos/19523960/pexels-photo-19523960/free-photo-of-black-and-white-portrait-of-a-man.jpeg?auto=compress&w=1200", + "photographer": "Alexandru Cojanu", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "미디엄 레이어드", + "웨이브" + ] + }, + { + "replaces_number": 27, + "hairstyle_id": "3ae493ce-1c5c-566c-95a2-ca69ace18f5f", + "file_id": "14365489-043d-5b3d-9480-85cfdf1ab9e1", + "upload_id": "9ada00ac-de14-515e-99f0-5c90c9c82bca", + "style_name": "사이드 스윕 숏컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/16098552.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/14365489-043d-5b3d-9480-85cfdf1ab9e1.jpg", + "sha256": "14a1d4fa561c0ad657a004b0836a2d16b875e0eb44630d06d9fda2099dc91038", + "file_size": 274261, + "width": 1200, + "height": 2138, + "source_photo_id": "16098552", + "source_url": "https://www.pexels.com/photo/young-redhead-in-a-blue-shirt-and-a-jacket-thrown-over-his-shoulder-16098552/", + "download_url": "https://images.pexels.com/photos/16098552/pexels-photo-16098552/free-photo-of-young-redhead-in-a-blue-shirt-and-a-jacket-thrown-over-his-shoulder.jpeg?auto=compress&w=1200", + "photographer": "Владимир Высоцкий", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "사이드파트" + ] + }, + { + "replaces_number": 28, + "hairstyle_id": "0c29ffac-9ee8-56b8-a7bf-13d46a0830a5", + "file_id": "e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f", + "upload_id": "785ef37b-cb07-5ddc-93f8-4ea39f6037ec", + "style_name": "컬리 하이톱", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/39241292.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/e0e0a3a7-31ba-5cd9-8a6b-5ba3a334df4f.jpg", + "sha256": "639db8c8913cfe961c1890b1cc6844a6441238ab3989a71689696b58e4a428fd", + "file_size": 173960, + "width": 1200, + "height": 1707, + "source_photo_id": "39241292", + "source_url": "https://www.pexels.com/photo/studio-portrait-of-a-confident-young-man-39241292/", + "download_url": "https://images.pexels.com/photos/39241292/pexels-photo-39241292/free-photo-of-studio-portrait-of-a-confident-young-man.jpeg?auto=compress&w=1200", + "photographer": "Maury Pires", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "컬리컷" + ] + }, + { + "replaces_number": 30, + "hairstyle_id": "3719ea70-8240-5652-8a16-02f8f5685b5f", + "file_id": "f35d850d-e1e9-5889-8536-170c1b4dd860", + "upload_id": "c9c7c93e-5a05-5c2b-a8ce-f108c0fe20b4", + "style_name": "내추럴 아프로", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/35249124.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/f35d850d-e1e9-5889-8536-170c1b4dd860.jpg", + "sha256": "762bfc399c1f086e30036a40fc1c89b1b273485b328c0e0a8b81ce38ab3075e8", + "file_size": 119849, + "width": 1200, + "height": 1500, + "source_photo_id": "35249124", + "source_url": "https://www.pexels.com/photo/portrait-of-a-young-black-man-with-earrings-35249124/", + "download_url": "https://images.pexels.com/photos/35249124/pexels-photo-35249124/free-photo-of-portrait-of-a-young-black-man-with-earrings.jpeg?auto=compress&w=1200", + "photographer": "Okiki Onipede", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "아프로" + ] + }, + { + "replaces_number": 31, + "hairstyle_id": "55a650e5-5a99-5ed7-bd78-2ede7a818ed6", + "file_id": "251f0cfd-e52c-5d25-b760-afcb97ef0211", + "upload_id": "9f567933-7385-59e0-b97f-caf3ba3deacc", + "style_name": "컬리 프린지 테이퍼", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/10617536.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/251f0cfd-e52c-5d25-b760-afcb97ef0211.jpg", + "sha256": "9b43790775b604fc4f67c6a2e27533b373c09420cb82879e69abf9b83f0f300d", + "file_size": 137001, + "width": 1200, + "height": 1800, + "source_photo_id": "10617536", + "source_url": "https://www.pexels.com/photo/a-man-in-white-shirt-and-white-suit-jacket-10617536/", + "download_url": "https://images.pexels.com/photos/10617536/pexels-photo-10617536.jpeg?auto=compress&w=1200", + "photographer": "cottonbro studio", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "컬리컷", + "테이퍼컷" + ] + }, + { + "replaces_number": 32, + "hairstyle_id": "f44a00c9-3c3f-5927-98f7-32966c2b137e", + "file_id": "9bd9a927-911f-5afb-8777-6c32d6ee9aa5", + "upload_id": "54c0e743-8f32-5c08-82f2-1cda777a6f2a", + "style_name": "웨이브 퀴프 언더컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/17928570.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/9bd9a927-911f-5afb-8777-6c32d6ee9aa5.jpg", + "sha256": "21b6e777221880b6df28df3ee160fd88ffc74ef85a9b2408b6685123ccd54832", + "file_size": 259470, + "width": 1200, + "height": 1509, + "source_photo_id": "17928570", + "source_url": "https://www.pexels.com/photo/man-with-dyed-hair-in-black-and-white-17928570/", + "download_url": "https://images.pexels.com/photos/17928570/pexels-photo-17928570/free-photo-of-man-with-dyed-hair-in-black-and-white.jpeg?auto=compress&w=1200", + "photographer": "Pexels User", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "언더컷", + "웨이브" + ] + }, + { + "replaces_number": 35, + "hairstyle_id": "090392ec-2fcb-56e3-ba4c-0063af2471d3", + "file_id": "8bf0e3b5-0402-5137-a403-03548286b0b6", + "upload_id": "f0904534-2e9e-556d-a986-5bf6ff5b1fe7", + "style_name": "로우 페이드 사이드파트", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/7781852.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/8bf0e3b5-0402-5137-a403-03548286b0b6.jpg", + "sha256": "2650054181fa1ed677aaceecccaaf941a73ef2cd8e6da215fc35a26074416472", + "file_size": 280909, + "width": 1200, + "height": 1783, + "source_photo_id": "7781852", + "source_url": "https://www.pexels.com/photo/man-in-black-barber-cape-7781852/", + "download_url": "https://images.pexels.com/photos/7781852/pexels-photo-7781852.jpeg?auto=compress&w=1200", + "photographer": "Leonardo Hidalgo", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "페이드컷", + "사이드파트" + ] + }, + { + "replaces_number": 36, + "hairstyle_id": "30482fbb-adf7-5843-a988-f340b85fa429", + "file_id": "790da07c-fb25-54ae-ad32-463ce2dbde3d", + "upload_id": "7ec1675f-2cdf-5fdb-8dd0-1ed72c348b14", + "style_name": "텍스처 멀릿컷", + "category": "MEDIUM", + "style_presentation": "MASCULINE", + "image_file": "images/11395986.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/790da07c-fb25-54ae-ad32-463ce2dbde3d.jpg", + "sha256": "7c8b840b273b0cddc88eef441feec04e51b7db1d5ef8a4ee74aa2f18fea09122", + "file_size": 191746, + "width": 1200, + "height": 1766, + "source_photo_id": "11395986", + "source_url": "https://www.pexels.com/photo/man-in-black-suit-jacket-11395986/", + "download_url": "https://images.pexels.com/photos/11395986/pexels-photo-11395986.jpeg?auto=compress&w=1200", + "photographer": "annmteu", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "멀릿컷", + "처피뱅" + ] + }, + { + "replaces_number": 37, + "hairstyle_id": "cb9713ac-a1b0-5e95-a303-e24bc4a425ac", + "file_id": "039a728d-7d09-53b4-840f-96e1411c62cf", + "upload_id": "505d88ac-a231-56fc-9346-41c2929675b8", + "style_name": "브러시백 멀릿컷", + "category": "MEDIUM", + "style_presentation": "MASCULINE", + "image_file": "images/30782416.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/039a728d-7d09-53b4-840f-96e1411c62cf.jpg", + "sha256": "b167b17eda6f2a2e329c0df9b23301feba7cfba80d474038f3c1079c977b8e94", + "file_size": 185320, + "width": 1200, + "height": 1799, + "source_photo_id": "30782416", + "source_url": "https://www.pexels.com/photo/portrait-of-bearded-man-with-stylish-haircut-30782416/", + "download_url": "https://images.pexels.com/photos/30782416/pexels-photo-30782416/free-photo-of-portrait-of-bearded-man-with-stylish-haircut.jpeg?auto=compress&w=1200", + "photographer": "dp singh Bhullar", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "멀릿컷" + ] + }, + { + "replaces_number": 38, + "hairstyle_id": "7d3fb671-6ae0-5bcb-a9f7-54d7c5f534f9", + "file_id": "6fb84335-f266-5528-8c37-6981a17aa42c", + "upload_id": "15e79121-5f0d-5114-a099-acbb6552ad82", + "style_name": "미디엄 언더컷 웨이브", + "category": "MEDIUM", + "style_presentation": "MASCULINE", + "image_file": "images/20742493.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/6fb84335-f266-5528-8c37-6981a17aa42c.jpg", + "sha256": "d74b29d9a7e1a27ffd58aac942d3222b459d9a6fa93c4510d2003b8e0a565c4c", + "file_size": 243852, + "width": 1200, + "height": 1800, + "source_photo_id": "20742493", + "source_url": "https://www.pexels.com/photo/a-man-wearing-sunglasses-20742493/", + "download_url": "https://images.pexels.com/photos/20742493/pexels-photo-20742493/free-photo-of-a-man-wearing-sunglasses.jpeg?auto=compress&w=1200", + "photographer": "Abhishek Shekhawat", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "언더컷", + "웨이브" + ] + }, + { + "replaces_number": 39, + "hairstyle_id": "b98199c9-df22-5914-8d6c-ff1c9818aa2e", + "file_id": "e0c83e60-8516-5dc7-b268-8180176c8196", + "upload_id": "42f61913-29d9-5d7f-95ad-cc7bceab6d0c", + "style_name": "버즈컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/12211686.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/e0c83e60-8516-5dc7-b268-8180176c8196.jpg", + "sha256": "f8e07a2f52fda943a20f947540fa6069f49713bf46af627d0a4c0a29a881dbad", + "file_size": 201305, + "width": 1200, + "height": 2133, + "source_photo_id": "12211686", + "source_url": "https://www.pexels.com/photo/portrait-of-handsome-man-with-stubble-12211686/", + "download_url": "https://images.pexels.com/photos/12211686/pexels-photo-12211686.jpeg?auto=compress&w=1200", + "photographer": "Reza Kargar", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 0, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "버즈컷" + ] + }, + { + "replaces_number": 56, + "hairstyle_id": "7f17a8fe-2b82-558e-969f-79a3563ce8fb", + "file_id": "cbc58c96-9253-5e84-a062-8c824986b638", + "upload_id": "79b782ca-78e9-5f5b-9743-01ecf7c8842e", + "style_name": "크루컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/16098697.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/cbc58c96-9253-5e84-a062-8c824986b638.jpg", + "sha256": "b79506f4a531bbcf9d144b4324b1149c354c34a3652c42abd4184f95e205eb94", + "file_size": 170500, + "width": 1200, + "height": 1800, + "source_photo_id": "16098697", + "source_url": "https://www.pexels.com/photo/smiling-man-in-gray-t-shirt-16098697/", + "download_url": "https://images.pexels.com/photos/16098697/pexels-photo-16098697/free-photo-of-smiling-man-in-gray-t-shirt.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "크루컷" + ] + }, + { + "replaces_number": 59, + "hairstyle_id": "87408dfc-91cf-53df-bfab-369e62e93e38", + "file_id": "805dcd76-e71c-5aa6-bbed-6dd100f64ec2", + "upload_id": "3139fd32-2fac-5c2e-9d11-a8e5d2c05c7e", + "style_name": "스킨 페이드 버즈컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/39559324.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/805dcd76-e71c-5aa6-bbed-6dd100f64ec2.jpg", + "sha256": "a61758d67be3a0238c10dd6e62eab0e4ee0cc80f6c2c959eff8a41998d490f20", + "file_size": 151545, + "width": 1200, + "height": 1800, + "source_photo_id": "39559324", + "source_url": "https://www.pexels.com/photo/modern-fade-haircut-with-sharp-beard-style-39559324/", + "download_url": "https://images.pexels.com/photos/39559324/pexels-photo-39559324/free-photo-of-modern-fade-haircut-with-sharp-beard-style.jpeg?auto=compress&w=1200", + "photographer": "Brian Silva", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY", + "CURLY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 0, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "버즈컷", + "페이드컷" + ] + }, + { + "replaces_number": 61, + "hairstyle_id": "b4b3e4fc-cd3e-5df2-ae97-4947e1076293", + "file_id": "ccdd16a6-c616-548c-8c54-2e0fe6c6f167", + "upload_id": "1421bae9-6b73-5762-90da-f6d7e9062268", + "style_name": "투블록 보울컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/944013.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/ccdd16a6-c616-548c-8c54-2e0fe6c6f167.jpg", + "sha256": "c47f24bcbb5d89291a4af08c67fd5c154cce7440a8096caa361f06514ebce693", + "file_size": 102651, + "width": 1200, + "height": 794, + "source_photo_id": "944013", + "source_url": "https://www.pexels.com/photo/man-in-black-collared-shirt-with-black-frame-eyeglasses-944013/", + "download_url": "https://images.pexels.com/photos/944013/pexels-photo-944013.jpeg?auto=compress&w=1200", + "photographer": "Min An", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "투블록", + "보울컷" + ] + }, + { + "replaces_number": 62, + "hairstyle_id": "b252732a-2d91-5c01-afd5-6d36f1bd3a92", + "file_id": "ad79a8cf-d74f-504e-ae76-b31e24155234", + "upload_id": "1e7208fe-9db6-50f4-86ff-6fc46edac019", + "style_name": "클래식 사이드파트", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/9168799.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/ad79a8cf-d74f-504e-ae76-b31e24155234.jpg", + "sha256": "1ea8a2d8ef6657569f9320cd395c1213f779bbfa60dda2e59727c5a1733b89e0", + "file_size": 98689, + "width": 1200, + "height": 1800, + "source_photo_id": "9168799", + "source_url": "https://www.pexels.com/photo/side-view-of-man-in-white-shirt-9168799/", + "download_url": "https://images.pexels.com/photos/9168799/pexels-photo-9168799.jpeg?auto=compress&w=1200", + "photographer": "Ron Lach", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "사이드파트" + ] + }, + { + "replaces_number": 64, + "hairstyle_id": "86c65822-8ea7-58c4-b115-d9924435ea68", + "file_id": "fe0565fa-297c-513b-80a1-111dde14aa81", + "upload_id": "8402b338-e9c2-544b-a054-1aa85685c10e", + "style_name": "스파이키 언더컷", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/11904768.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/fe0565fa-297c-513b-80a1-111dde14aa81.jpg", + "sha256": "bd62355f481bbd647443739acf1e32ac84774039d088e4f449c1ab288c635f09", + "file_size": 150811, + "width": 1200, + "height": 1680, + "source_photo_id": "11904768", + "source_url": "https://www.pexels.com/photo/a-man-in-black-denim-jacket-11904768/", + "download_url": "https://images.pexels.com/photos/11904768/pexels-photo-11904768.jpeg?auto=compress&w=1200", + "photographer": "MENG MEDIA", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "언더컷", + "스파이키컷" + ] + }, + { + "replaces_number": 67, + "hairstyle_id": "8a9d43c6-cf38-5cc3-b038-94fe0f689de7", + "file_id": "4807dce0-83fe-53f6-87f2-3679c38cfd1b", + "upload_id": "b84fe71b-42dd-540f-98d4-e2cd61b7b630", + "style_name": "슬릭백 스타일", + "category": "SHORT", + "style_presentation": "MASCULINE", + "image_file": "images/8799957.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/4807dce0-83fe-53f6-87f2-3679c38cfd1b.jpg", + "sha256": "c3188ef87dc380f15d6ae680e51ab251f313b53a64400c84b657321cd65b984c", + "file_size": 137009, + "width": 1200, + "height": 1800, + "source_photo_id": "8799957", + "source_url": "https://www.pexels.com/photo/man-in-crew-neck-shirt-8799957/", + "download_url": "https://images.pexels.com/photos/8799957/pexels-photo-8799957.jpeg?auto=compress&w=1200", + "photographer": "Minh Tri", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "슬릭백" + ] + }, + { + "replaces_number": 68, + "hairstyle_id": "c17d60af-6144-5d44-9658-eda2e5ad638d", + "file_id": "8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4", + "upload_id": "b7784df8-a82e-5c32-8eda-8d6de7315471", + "style_name": "센터파트 내추럴 플로우", + "category": "MEDIUM", + "style_presentation": "MASCULINE", + "image_file": "images/4736680.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/8bfcc7ca-ef75-5eb4-b7cc-b417968c7ac4.jpg", + "sha256": "c9860ced9e54ed4d8e0da5d4e98b315fa9e8a1cb8a0319da98b5f3f32e4af79e", + "file_size": 232465, + "width": 1200, + "height": 1800, + "source_photo_id": "4736680", + "source_url": "https://www.pexels.com/photo/man-in-white-dress-shirt-4736680/", + "download_url": "https://images.pexels.com/photos/4736680/pexels-photo-4736680.jpeg?auto=compress&w=1200", + "photographer": "cottonbro studio", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "가르마컷", + "리프컷" + ] + }, + { + "replaces_number": 69, + "hairstyle_id": "06cc4930-e540-5d6f-b284-1e08b1818a1c", + "file_id": "6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c", + "upload_id": "246e7322-0fa8-5d64-a199-7d95950ad88c", + "style_name": "커튼뱅 리프컷", + "category": "MEDIUM", + "style_presentation": "MASCULINE", + "image_file": "images/9775640.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/6b6cd640-a67d-5c8b-b638-bd7ed7d2d15c.jpg", + "sha256": "ad8d88b85e804ce6885f142592c4d910bcaf4877760e8670356297e82556f28f", + "file_size": 45784, + "width": 1200, + "height": 800, + "source_photo_id": "9775640", + "source_url": "https://www.pexels.com/photo/close-up-shot-of-a-man-in-a-green-hoodie-9775640/", + "download_url": "https://images.pexels.com/photos/9775640/pexels-photo-9775640.jpeg?auto=compress&w=1200", + "photographer": "SHVETS production", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "리프컷", + "사이드뱅" + ] + }, + { + "replaces_number": 70, + "hairstyle_id": "76145924-61ee-52ca-8fb6-faf5392da1d5", + "file_id": "8da7f475-29f4-5218-9572-f1bc81b89985", + "upload_id": "d39c2916-e45c-5ac8-97a2-847a0731519b", + "style_name": "옴브레 롱 글램 웨이브", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/20046790.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/8da7f475-29f4-5218-9572-f1bc81b89985.jpg", + "sha256": "a60c0ebd24d1ef0726b65b5e50ef212b4b3fbe1b4f03beae2cb355b41de1bf30", + "file_size": 204630, + "width": 1200, + "height": 1800, + "source_photo_id": "20046790", + "source_url": "https://www.pexels.com/photo/portrait-of-brunette-woman-20046790/", + "download_url": "https://images.pexels.com/photos/20046790/pexels-photo-20046790/free-photo-of-portrait-of-brunette-woman.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "글램펌" + ] + }, + { + "replaces_number": 72, + "hairstyle_id": "447ef348-7a08-5565-b345-de7ba9aa77fd", + "file_id": "830fef5a-219b-5a3a-894a-9c501d7f2ee4", + "upload_id": "43444ef5-7aad-5467-9f51-5e8b5a2203ee", + "style_name": "사이드뱅 롱 레이어드", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/19465049.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/830fef5a-219b-5a3a-894a-9c501d7f2ee4.jpg", + "sha256": "78b483d2523615d352db726eb7d70ac35142265d934256695c2f48076be56fcf", + "file_size": 159658, + "width": 1200, + "height": 1800, + "source_photo_id": "19465049", + "source_url": "https://www.pexels.com/photo/woman-sitting-in-black-clothes-19465049/", + "download_url": "https://images.pexels.com/photos/19465049/pexels-photo-19465049/free-photo-of-woman-sitting-in-black-clothes.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "사이드뱅" + ] + }, + { + "replaces_number": 73, + "hairstyle_id": "cbf47ccd-6a33-5cc1-963d-6dcd61fd0189", + "file_id": "ed1707b3-54c0-59a0-b921-f80d7d84beca", + "upload_id": "19711382-8e2e-585e-94cb-4a692c94b97d", + "style_name": "발레아쥬 중단발 웨이브", + "category": "MEDIUM", + "style_presentation": "FEMININE", + "image_file": "images/17328861.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/ed1707b3-54c0-59a0-b921-f80d7d84beca.jpg", + "sha256": "8660c7d1dbadccb968483b72fe6e1b37ba016c39621d00ef3994c37d9d6fcf62", + "file_size": 251228, + "width": 1200, + "height": 1800, + "source_photo_id": "17328861", + "source_url": "https://www.pexels.com/photo/portrait-of-a-brunette-woman-17328861/", + "download_url": "https://images.pexels.com/photos/17328861/pexels-photo-17328861/free-photo-of-portrait-of-a-brunette-woman.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "미디엄 레이어드", + "웨이브" + ] + }, + { + "replaces_number": 76, + "hairstyle_id": "02310a32-527e-599b-aafd-a64c63398d68", + "file_id": "0aa49435-f662-5367-9c31-f2d70c4cbf18", + "upload_id": "d49ffc6f-8f9d-5181-a1c4-983910a11f9d", + "style_name": "페이스프레이밍 레이어드", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/15868761.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/0aa49435-f662-5367-9c31-f2d70c4cbf18.jpg", + "sha256": "6cb04d4009bccb8ba729f368320ab46a9e34186b7ec577efdbe3a4feb18b5931", + "file_size": 338540, + "width": 1200, + "height": 1800, + "source_photo_id": "15868761", + "source_url": "https://www.pexels.com/photo/woman-with-hairdo-in-saloon-15868761/", + "download_url": "https://images.pexels.com/photos/15868761/pexels-photo-15868761/free-photo-of-woman-with-hairdo-in-saloon.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "사이드뱅" + ] + }, + { + "replaces_number": 77, + "hairstyle_id": "4972ab0a-e635-58d1-85e3-9889c6cce4a7", + "file_id": "12eea25f-cad4-59ce-9f21-18a8f268adc2", + "upload_id": "edb50534-704a-530e-9cdf-190477b5c48b", + "style_name": "커튼뱅 롱 스트레이트", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/30661047.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/12eea25f-cad4-59ce-9f21-18a8f268adc2.jpg", + "sha256": "fc5e36eb6318655c3f440677e040d8390258759ddb73031960ad2c7624d4661b", + "file_size": 195920, + "width": 1200, + "height": 1800, + "source_photo_id": "30661047", + "source_url": "https://www.pexels.com/photo/young-woman-with-long-straight-hair-in-salon-30661047/", + "download_url": "https://images.pexels.com/photos/30661047/pexels-photo-30661047/free-photo-of-young-woman-with-long-straight-hair-in-salon.jpeg?auto=compress&w=1200", + "photographer": "Ông Ngọc Dư", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHEST", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "긴 생머리", + "사이드뱅" + ] + }, + { + "replaces_number": 78, + "hairstyle_id": "42d2fb31-6285-5aa4-864f-8fbe10e75a5c", + "file_id": "cc7d31a0-a676-5d7c-a8d6-ee960a834587", + "upload_id": "82fd3751-228a-522e-a492-276ef3dec3f3", + "style_name": "블런트 프린지 픽시", + "category": "SHORT", + "style_presentation": "FEMININE", + "image_file": "images/28969181.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/cc7d31a0-a676-5d7c-a8d6-ee960a834587.jpg", + "sha256": "bce3e0bbc5aa0acef47f2707a162a40b41074f2c64e8025f57612cb7f78f3c39", + "file_size": 167927, + "width": 1200, + "height": 1800, + "source_photo_id": "28969181", + "source_url": "https://www.pexels.com/photo/chic-portrait-of-person-with-blonde-pixie-haircut-28969181/", + "download_url": "https://images.pexels.com/photos/28969181/pexels-photo-28969181/free-photo-of-chic-portrait-of-person-with-blonde-pixie-haircut.jpeg?auto=compress&w=1200", + "photographer": "pedro furtado", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "픽시컷", + "처피뱅" + ] + }, + { + "replaces_number": 80, + "hairstyle_id": "f055858a-07fe-5a79-a257-b7ccc3dab7f8", + "file_id": "d37fc871-3166-573f-bd37-579e9fd9d993", + "upload_id": "f4191642-47a1-5369-8e2c-541ed28ad0ff", + "style_name": "볼륨 텍스처 픽시", + "category": "SHORT", + "style_presentation": "FEMININE", + "image_file": "images/3636489.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/d37fc871-3166-573f-bd37-579e9fd9d993.jpg", + "sha256": "7beae5de025bf361346eef67d276513ef303d49882432601a5c4e79e974aa8bf", + "file_size": 219156, + "width": 1200, + "height": 1800, + "source_photo_id": "3636489", + "source_url": "https://www.pexels.com/photo/close-up-photo-of-a-woman-with-short-hair-3636489/", + "download_url": "https://images.pexels.com/photos/3636489/pexels-photo-3636489.jpeg?auto=compress&w=1200", + "photographer": "Marielle Capinianes", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "픽시컷" + ] + }, + { + "replaces_number": 81, + "hairstyle_id": "8167d07c-eb7e-5632-878a-0df3e0ac5ee7", + "file_id": "c1d14fa0-b538-5746-84bd-0ccaf03a54f0", + "upload_id": "0404e286-42d3-5a11-8a27-4ebb523c9daf", + "style_name": "사이드 프린지 픽시", + "category": "SHORT", + "style_presentation": "FEMININE", + "image_file": "images/6589540.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/c1d14fa0-b538-5746-84bd-0ccaf03a54f0.jpg", + "sha256": "12695486bf01e632a74f2a83aa2ff16c261e6bf766209cde39a2213d43486cf8", + "file_size": 126254, + "width": 1200, + "height": 1600, + "source_photo_id": "6589540", + "source_url": "https://www.pexels.com/photo/young-trendy-woman-with-piercing-and-short-red-hair-6589540/", + "download_url": "https://images.pexels.com/photos/6589540/pexels-photo-6589540.jpeg?auto=compress&w=1200", + "photographer": "Daylane Oliveira", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "픽시컷", + "사이드뱅" + ] + }, + { + "replaces_number": 82, + "hairstyle_id": "6abf9427-67ac-5754-a2f3-a29c50a8f4b6", + "file_id": "1f8d371f-ba06-5d7d-9425-d5afe5a0c36a", + "upload_id": "098cfb06-257a-5740-b3c9-8d0af011a90d", + "style_name": "마이크로 프린지 숏컷", + "category": "SHORT", + "style_presentation": "FEMININE", + "image_file": "images/11532757.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/1f8d371f-ba06-5d7d-9425-d5afe5a0c36a.jpg", + "sha256": "728482538e48a69a5ae57f398314f8fb41c58401c2489ee3c7178cbab0cd0c38", + "file_size": 180375, + "width": 1200, + "height": 1600, + "source_photo_id": "11532757", + "source_url": "https://www.pexels.com/photo/woman-in-white-shirt-wearing-silver-earring-11532757/", + "download_url": "https://images.pexels.com/photos/11532757/pexels-photo-11532757.jpeg?auto=compress&w=1200", + "photographer": "Sidicley Santana dos santos", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "픽시컷", + "처피뱅" + ] + }, + { + "replaces_number": 84, + "hairstyle_id": "b1fdcdba-f935-5f0d-8fba-e9b56f3003fc", + "file_id": "6d90b2d5-8a82-5966-b7af-60c76f6a8525", + "upload_id": "5013a16d-a2d8-5292-975c-17bd8373dfa3", + "style_name": "비대칭 언더컷 픽시", + "category": "SHORT", + "style_presentation": "FEMININE", + "image_file": "images/9197236.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/6d90b2d5-8a82-5966-b7af-60c76f6a8525.jpg", + "sha256": "ae0e8b2ba3ce49268254c6758b04428c9a9ce06ca75ff2ad2efeb8ec763985a1", + "file_size": 224126, + "width": 1200, + "height": 1799, + "source_photo_id": "9197236", + "source_url": "https://www.pexels.com/photo/woman-with-pixie-cut-hair-9197236/", + "download_url": "https://images.pexels.com/photos/9197236/pexels-photo-9197236.jpeg?auto=compress&w=1200", + "photographer": "Юлиана Маринина", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "SHORT", + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "SHORT", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "픽시컷", + "언더컷" + ] + }, + { + "replaces_number": 85, + "hairstyle_id": "e0ef9b77-ea49-5d67-80da-673fc665e394", + "file_id": "6c2c27c3-210a-5ca4-b322-09aad446f639", + "upload_id": "3c4f624b-a12f-5b4b-846e-e059ee90b8f3", + "style_name": "블런트 보브", + "category": "BOB", + "style_presentation": "FEMININE", + "image_file": "images/6822023.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/6c2c27c3-210a-5ca4-b322-09aad446f639.jpg", + "sha256": "a3fb86a403dcd5115dca583bd3e9858035b68955e445588a91c7fe9c7fe864af", + "file_size": 209288, + "width": 1200, + "height": 1520, + "source_photo_id": "6822023", + "source_url": "https://www.pexels.com/photo/back-view-shot-of-a-person-with-short-hair-wearing-black-shirt-6822023/", + "download_url": "https://images.pexels.com/photos/6822023/pexels-photo-6822023.jpeg?auto=compress&w=1200", + "photographer": "Elīna Arāja", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "클래식 보브", + "태슬컷" + ] + }, + { + "replaces_number": 86, + "hairstyle_id": "ffe39c84-1b70-58f0-979c-b7474c76888a", + "file_id": "b9712588-5c6d-5aeb-b826-3db036524a04", + "upload_id": "94b77999-44b8-58dd-ba91-8c5b81a16b11", + "style_name": "시스루뱅 보브", + "category": "BOB", + "style_presentation": "FEMININE", + "image_file": "images/20304296.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/b9712588-5c6d-5aeb-b826-3db036524a04.jpg", + "sha256": "4754adc620a9adb8319e6abd26f1a1bd803ecfae9bb13da2f3b329b9c7fea792", + "file_size": 174509, + "width": 1200, + "height": 1961, + "source_photo_id": "20304296", + "source_url": "https://www.pexels.com/photo/portrait-of-woman-with-hand-on-cheek-20304296/", + "download_url": "https://images.pexels.com/photos/20304296/pexels-photo-20304296/free-photo-of-portrait-of-woman-with-hand-on-cheek.jpeg?auto=compress&w=1200", + "photographer": "xomidov Photo", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "클래식 보브", + "시스루뱅" + ] + }, + { + "replaces_number": 87, + "hairstyle_id": "16c7597c-2c40-51e1-a754-ef4fe35aa2b3", + "file_id": "4333aaa0-02ed-531d-b768-300535e05b0d", + "upload_id": "8c9e83bb-3dc1-5e1e-9d5d-870c7f4eff78", + "style_name": "사이드뱅 클래식 보브", + "category": "BOB", + "style_presentation": "FEMININE", + "image_file": "images/17135759.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/4333aaa0-02ed-531d-b768-300535e05b0d.jpg", + "sha256": "79caeede28822f77a66d1756b7efba5b8e0a9ff26f148c4cce89c3f2cf981c0a", + "file_size": 121672, + "width": 1200, + "height": 1500, + "source_photo_id": "17135759", + "source_url": "https://www.pexels.com/photo/portrait-of-an-elegant-brunette-17135759/", + "download_url": "https://images.pexels.com/photos/17135759/pexels-photo-17135759/free-photo-of-portrait-of-an-elegant-brunette.jpeg?auto=compress&w=1200", + "photographer": "Bardia Golzar", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "클래식 보브", + "사이드뱅" + ] + }, + { + "replaces_number": 88, + "hairstyle_id": "c9442c21-485b-5d64-87c3-a97b491e3702", + "file_id": "79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9", + "upload_id": "9cf167c2-79cd-5bd6-8e6f-f9b12596e478", + "style_name": "사이드파트 원랭스 보브", + "category": "BOB", + "style_presentation": "FEMININE", + "image_file": "images/5301009.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/79ab601a-0ef7-5fc1-ae0d-f01f12d69fd9.jpg", + "sha256": "22ee56b41a12d65b621d0ee0a00bdc18a5ab039c1594abffcb643aec1d0179dc", + "file_size": 249953, + "width": 1200, + "height": 1800, + "source_photo_id": "5301009", + "source_url": "https://www.pexels.com/photo/young-woman-wearing-a-white-turtleneck-5301009/", + "download_url": "https://images.pexels.com/photos/5301009/pexels-photo-5301009.jpeg?auto=compress&w=1200", + "photographer": "Guan Yoanda", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "클래식 보브" + ] + }, + { + "replaces_number": 89, + "hairstyle_id": "e01d6760-17b8-5cd4-97de-c604322e304d", + "file_id": "61f943fd-f574-5894-8ea6-916149971fae", + "upload_id": "6c3a844d-1561-523a-8fc5-4594a020b2c8", + "style_name": "센터파트 슬릭 보브", + "category": "BOB", + "style_presentation": "FEMININE", + "image_file": "images/8182243.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/61f943fd-f574-5894-8ea6-916149971fae.jpg", + "sha256": "c1e880d9eef714180b0313210e75bf39841b2716c1dc658ffefaf1a198d2c080", + "file_size": 133130, + "width": 1200, + "height": 1800, + "source_photo_id": "8182243", + "source_url": "https://www.pexels.com/photo/close-up-photo-of-woman-wearing-off-shoulder-top-8182243/", + "download_url": "https://images.pexels.com/photos/8182243/pexels-photo-8182243.jpeg?auto=compress&w=1200", + "photographer": "RDNE Stock project", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 5, + "management_difficulty": "LOW", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "클래식 보브", + "태슬컷" + ] + }, + { + "replaces_number": 91, + "hairstyle_id": "abcb8ccb-9044-5c1b-954e-2c222fc5a701", + "file_id": "917bcbd1-29cb-514c-95ab-1f6240f36631", + "upload_id": "3a5550dd-50a0-5f36-a02f-f69c8aa75b3f", + "style_name": "커튼 레이어드 블로아웃", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/18367694.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/917bcbd1-29cb-514c-95ab-1f6240f36631.jpg", + "sha256": "372cac673947612bda42e1ccba01e005437c09532b188abffb1869e0d27067c2", + "file_size": 196822, + "width": 1200, + "height": 1800, + "source_photo_id": "18367694", + "source_url": "https://www.pexels.com/photo/young-woman-with-a-layered-haircut-sitting-in-a-hair-salon-18367694/", + "download_url": "https://images.pexels.com/photos/18367694/pexels-photo-18367694/free-photo-of-young-woman-with-a-layered-haircut-sitting-in-a-hair-salon.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "사이드뱅" + ] + }, + { + "replaces_number": 92, + "hairstyle_id": "fb32cea4-c14f-5c7e-a0d0-2225caf4361c", + "file_id": "6c25c799-986b-5296-a5fb-2414409f7353", + "upload_id": "3e6d548d-9ea9-5c71-9184-6c2db305744c", + "style_name": "하이라이트 롱 S컬", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/18189268.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/6c25c799-986b-5296-a5fb-2414409f7353.jpg", + "sha256": "05e8acc2e2a72b61379abcd9304f2eb06a4cfe2260ba2ad801a2a663f6822c49", + "file_size": 277049, + "width": 1200, + "height": 1800, + "source_photo_id": "18189268", + "source_url": "https://www.pexels.com/photo/portrait-of-woman-18189268/", + "download_url": "https://images.pexels.com/photos/18189268/pexels-photo-18189268/free-photo-of-portrait-of-woman.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "웨이브" + ] + }, + { + "replaces_number": 93, + "hairstyle_id": "5b2448da-3946-5de8-9e22-a1584575acda", + "file_id": "144afb9a-a898-5f3d-adc1-33b43c08b879", + "upload_id": "7448211d-037e-5794-9f4b-5b70c2d81f8e", + "style_name": "롱 레이어드 C컬", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/17163786.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/144afb9a-a898-5f3d-adc1-33b43c08b879.jpg", + "sha256": "1811475fed1dffb1f1e8dbf3fdd9938de9e548c9facb36e1f6d39a8aa7b697a9", + "file_size": 192811, + "width": 1200, + "height": 1800, + "source_photo_id": "17163786", + "source_url": "https://www.pexels.com/photo/portrait-of-brunette-woman-17163786/", + "download_url": "https://images.pexels.com/photos/17163786/pexels-photo-17163786/free-photo-of-portrait-of-brunette-woman.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드" + ] + }, + { + "replaces_number": 94, + "hairstyle_id": "c0dcc01b-d923-56b0-ba41-3e7f060dac05", + "file_id": "621b4131-8d40-57a6-8c47-2a6d665b476d", + "upload_id": "803d8165-0bb4-5d47-97cf-233b62cbe55a", + "style_name": "롱 페더드 레이어드", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/18189265.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/621b4131-8d40-57a6-8c47-2a6d665b476d.jpg", + "sha256": "ec0c264b5230985b024326a8ecfc1cfc3fe002a01ac022c4d83abc9c7a260e55", + "file_size": 189980, + "width": 1200, + "height": 1800, + "source_photo_id": "18189265", + "source_url": "https://www.pexels.com/photo/portrait-of-woman-with-black-hair-18189265/", + "download_url": "https://images.pexels.com/photos/18189265/pexels-photo-18189265/free-photo-of-portrait-of-woman-with-black-hair.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드" + ] + }, + { + "replaces_number": 95, + "hairstyle_id": "cc25d0ff-ec17-5eab-9dc3-95b0fcc14ad0", + "file_id": "e51b4c2e-ce5e-5817-baff-1280bd64ccf5", + "upload_id": "06255242-2663-52ee-8248-ed33424e818f", + "style_name": "볼륨 레이어드 블로아웃", + "category": "MEDIUM", + "style_presentation": "FEMININE", + "image_file": "images/17164030.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/e51b4c2e-ce5e-5817-baff-1280bd64ccf5.jpg", + "sha256": "1ada903281efa4a7723b76708b25db14e60e4e80de4fe9984c2017a55a404d3a", + "file_size": 228745, + "width": 1200, + "height": 1800, + "source_photo_id": "17164030", + "source_url": "https://www.pexels.com/photo/brunette-woman-posing-with-arms-crossed-17164030/", + "download_url": "https://images.pexels.com/photos/17164030/pexels-photo-17164030/free-photo-of-brunette-woman-posing-with-arms-crossed.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "미디엄 레이어드" + ] + }, + { + "replaces_number": 96, + "hairstyle_id": "31717139-b772-5190-95ad-bdad71d6aaed", + "file_id": "670329bd-c450-504d-9598-9fea62599e72", + "upload_id": "ecc49cc9-b20f-52d5-934f-111ff12e24b2", + "style_name": "사이드뱅 미디엄 C컬", + "category": "MEDIUM", + "style_presentation": "FEMININE", + "image_file": "images/15908498.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/670329bd-c450-504d-9598-9fea62599e72.jpg", + "sha256": "4f9e457294dafd0582ef81708770f93c3974a6cacdaaf5faa164c8df9e53dead", + "file_size": 215473, + "width": 1200, + "height": 1800, + "source_photo_id": "15908498", + "source_url": "https://www.pexels.com/photo/smiling-woman-in-new-balance-sweatshirt-15908498/", + "download_url": "https://images.pexels.com/photos/15908498/pexels-photo-15908498/free-photo-of-smiling-woman-in-new-balance-sweatshirt.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "미디엄 레이어드", + "사이드뱅" + ] + }, + { + "replaces_number": 97, + "hairstyle_id": "9dde44be-0010-5f35-880b-35075b1c9ca4", + "file_id": "56bba053-3ad3-562c-b81f-c46044e26049", + "upload_id": "bc7e5dca-dc8a-5992-bad0-24f87ad7ee32", + "style_name": "롱 캐스케이드 웨이브", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/4584409.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/56bba053-3ad3-562c-b81f-c46044e26049.jpg", + "sha256": "0ae6ff3c97af5b38607afdb24e3f59688f7c0bb81edbbf8d982b67656249bfd2", + "file_size": 210998, + "width": 1200, + "height": 1804, + "source_photo_id": "4584409", + "source_url": "https://www.pexels.com/photo/woman-in-black-sleeveless-top-holding-hair-4584409/", + "download_url": "https://images.pexels.com/photos/4584409/pexels-photo-4584409.jpeg?auto=compress&w=1200", + "photographer": "Ketut Subiyanto", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "웨이브" + ] + }, + { + "replaces_number": 98, + "hairstyle_id": "5aa1cd83-3951-5ae7-999f-ddd765f75108", + "file_id": "5657847a-f497-5fd1-8ca5-7bcb0353e152", + "upload_id": "695307d0-7ace-5d07-a337-6f68f09ed9bd", + "style_name": "사이드뱅 페더드컷", + "category": "LONG", + "style_presentation": "FEMININE", + "image_file": "images/17163935.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/5657847a-f497-5fd1-8ca5-7bcb0353e152.jpg", + "sha256": "44f24d6b508713f08c7f9c52f34da78d807467dc44422d47cdbbe768c2e25ee7", + "file_size": 295705, + "width": 1200, + "height": 1800, + "source_photo_id": "17163935", + "source_url": "https://www.pexels.com/photo/smiling-brunette-woman-17163935/", + "download_url": "https://images.pexels.com/photos/17163935/pexels-photo-17163935/free-photo-of-smiling-brunette-woman.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 15, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "롱 레이어드", + "사이드뱅" + ] + }, + { + "replaces_number": 99, + "hairstyle_id": "e4eff9f7-6ee8-5023-95c1-fdbad8a771e7", + "file_id": "3763f094-afc0-5b98-9160-4ef220ddae94", + "upload_id": "474249ec-b38e-5af8-b4d8-6f3d50fe4db1", + "style_name": "미디엄 페더드 C컬", + "category": "MEDIUM", + "style_presentation": "FEMININE", + "image_file": "images/20046783.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/3763f094-afc0-5b98-9160-4ef220ddae94.jpg", + "sha256": "f1cc1f414b115d6e9ee4d18724a8fc4d5af265f094887b0a207f368ce07a0107", + "file_size": 242682, + "width": 1200, + "height": 1800, + "source_photo_id": "20046783", + "source_url": "https://www.pexels.com/photo/portrait-of-brunette-woman-20046783/", + "download_url": "https://images.pexels.com/photos/20046783/pexels-photo-20046783/free-photo-of-portrait-of-brunette-woman.jpeg?auto=compress&w=1200", + "photographer": "TONI&GUY PONDICHERRY", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_SHOULDER", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "미디엄 레이어드" + ] + }, + { + "replaces_number": 100, + "hairstyle_id": "2a7afeef-18a9-5e09-9516-a248e68b4090", + "file_id": "4f08b7f6-72c1-50be-aa2d-a9f31ef73973", + "upload_id": "204febd9-3b29-5b90-b6c2-2e3dc2fcff46", + "style_name": "숏 샤기 멀릿컷", + "category": "MEDIUM", + "style_presentation": "FEMININE", + "image_file": "images/34332682.jpg", + "object_key": "HAIRSTYLE_THUMBNAIL/curated-20261016/4f08b7f6-72c1-50be-aa2d-a9f31ef73973.jpg", + "sha256": "2bca2225c39b9b46ff61db5584a9cbbd4ba86ca815443eedb65ce01de7d9fc51", + "file_size": 56767, + "width": 1200, + "height": 675, + "source_photo_id": "34332682", + "source_url": "https://www.pexels.com/photo/bright-portrait-of-woman-with-modern-hairstyle-34332682/", + "download_url": "https://images.pexels.com/photos/34332682/pexels-photo-34332682/free-photo-of-bright-portrait-of-woman-with-modern-hairstyle.jpeg?auto=compress&w=1200", + "photographer": "Ailín Policano", + "license": "Pexels License", + "license_url": "https://www.pexels.com/license/", + "terms_url": "https://www.pexels.com/terms-of-service/", + "license_checked_on": "2026-10-16", + "page_license_label": "Free", + "visual_review": "PASS", + "professional_review": false, + "service_types": [ + "CUT", + "STYLING" + ], + "compatible_hair_lengths": [ + "BELOW_CHIN", + "BELOW_SHOULDER", + "BELOW_CHEST" + ], + "minimum_hair_length": "BELOW_CHIN", + "compatible_hair_types": [ + "STRAIGHT", + "WAVY" + ], + "compatible_hair_thicknesses": [ + "THIN", + "NORMAL", + "THICK" + ], + "compatible_hair_conditions": [ + "HEALTHY", + "NORMAL", + "DAMAGED", + "SEVERELY_DAMAGED" + ], + "contraindicated_hair_conditions": [], + "estimated_daily_care_minutes": 10, + "management_difficulty": "MEDIUM", + "chemical_stress_level": "LOW", + "editorial_priority": 50, + "tags": [ + "샤기컷", + "멀릿컷" + ] + } + ], + "sql_sha256": "ed9fdf6f121d844147279ff931f19e2963aa1a797e55c6da7ba7681ae62c0111" +} diff --git a/scripts/catalog.py b/scripts/catalog.py new file mode 100644 index 00000000..0da82aad --- /dev/null +++ b/scripts/catalog.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Validate or apply the reviewed, immutable catalogue bundle. No application secrets are logged.""" +import argparse +import collections +import hashlib +import json +import os +from pathlib import Path +import subprocess +import sys + + +def validate(bundle): + bundle = Path(bundle) + data = json.loads((bundle / 'manifest.json').read_text()) + entries = data['entries'] + assert data['release'] == 'curated-20261016' + expected = {2,5,6,7,8,9,23,27,28,30,31,32,35,36,37,38,39,56,59,61,62,64,67,68,69,70,72,73,76,77,78,80,81,82,84,85,86,87,88,89,91,92,93,94,95,96,97,98,99,100} + assert set(data['replacement_numbers']) == expected + assert {e['replaces_number'] for e in entries} == expected + assert set(data['duplicate_numbers']) == set(range(40,49)) - {47} + assert len(entries) == 50 + assert collections.Counter(e['style_presentation'] for e in entries) == {'MASCULINE':25,'FEMININE':25} + for key in ['hairstyle_id','file_id','upload_id','object_key','source_photo_id','sha256','style_name']: + assert len({e[key] for e in entries}) == 50, ('duplicate', key) + levels = ['SHORT','BELOW_CHIN','BELOW_SHOULDER','BELOW_CHEST'] + for e in entries: + path = (bundle / e['image_file']).resolve() + assert path.is_relative_to(bundle.resolve() / 'images') + content = path.read_bytes() + assert content[:3] == b'\xff\xd8\xff', e['image_file'] + assert hashlib.sha256(content).hexdigest() == e['sha256'], e['image_file'] + assert len(content) == e['file_size'] + assert min(e['width'],e['height']) >= 500 + assert e['source_url'].startswith('https://www.pexels.com/photo/') + assert e['photographer'] and e['license'] == 'Pexels License' and e['page_license_label'] == 'Free' + assert e['minimum_hair_length'] in levels + assert e['compatible_hair_lengths'] == levels[levels.index(e['minimum_hair_length']):] + assert e['category'] == 'SHORT' or e['minimum_hair_length'] != 'SHORT' + assert e['management_difficulty'] in {'LOW','MEDIUM','HIGH'} + assert set(e['service_types']) <= {'CUT','STYLING','PERM','COLOR','TREATMENT'} + assert len(e['style_name']) <= 30 + assert e['object_key'].startswith('HAIRSTYLE_THUMBNAIL/'+data['release']+'/') + sql = (bundle/'apply.sql').read_bytes() + assert hashlib.sha256(sql).hexdigest() == data['sql_sha256'] + assert sql.decode().startswith('BEGIN;') and sql.decode().rstrip().endswith('COMMIT;') + print(json.dumps({'validated':len(entries),'retired':58,'representative':47,'categories':dict(collections.Counter(e['category'] for e in entries))},ensure_ascii=False)) + return data + + +def fetch_images(bundle): + """Fetch original licensed assets without republishing image binaries in Git.""" + from concurrent.futures import ThreadPoolExecutor + from urllib.parse import urlparse + bundle = Path(bundle).resolve() + data = json.loads((bundle/'manifest.json').read_text()) + def fetch(e): + target = (bundle/e['image_file']).resolve() + assert target.is_relative_to(bundle/'images') + url = urlparse(e['download_url']) + assert url.scheme == 'https' and url.hostname == 'images.pexels.com' + assert url.path.startswith('/photos/'+e['source_photo_id']+'/') and url.path.endswith('.jpeg'), e['source_photo_id'] + if target.exists(): + content = target.read_bytes() + else: + result = subprocess.run(['curl','--fail','--silent','--show-error','--location','--proto','=https', + '--retry','3','--max-time','60',e['download_url']],capture_output=True,check=True) + content = result.stdout + assert hashlib.sha256(content).hexdigest() == e['sha256'], 'Source image changed: '+e['source_photo_id'] + assert len(content) == e['file_size'] + if not target.exists(): + target.parent.mkdir(parents=True,exist_ok=True) + target.write_bytes(content) + with ThreadPoolExecutor(max_workers=4) as pool: + list(pool.map(fetch,data['entries'])) + + +def psql(sql): + result = subprocess.run(['docker','exec','-i','heddy-v2-postgres','sh','-c', + 'exec psql -X -U "$POSTGRES_USER" -d heddy_v2 -qAt -v ON_ERROR_STOP=1'], + input=sql, text=True, capture_output=True) + if result.returncode: + raise RuntimeError('Catalogue SQL failed: '+result.stderr[-1500:]) + return result.stdout + + +def apply(bundle, data): + import boto3 + from botocore.exceptions import ClientError + inspect = json.loads(subprocess.check_output(['docker','inspect','heddy-v2-app']))[0] + env = dict(v.split('=',1) for v in inspect['Config']['Env'] if '=' in v) + options = {'region_name': env.get('AWS_S3_REGION','ap-northeast-2')} + if env.get('AWS_S3_ACCESS_KEY') and env.get('AWS_S3_SECRET_KEY'): + options.update(aws_access_key_id=env['AWS_S3_ACCESS_KEY'],aws_secret_access_key=env['AWS_S3_SECRET_KEY']) + client = boto3.client('s3',**options) + bucket = env['AWS_S3_BUCKET'] + # Preflight DB identity before uploads; only the 100 reviewed SYSTEM assets are in scope. + original = {o['hairstyle_id']:o for o in data['original']} + ids = ','.join("'"+i+"'::uuid" for i in original) + snapshot = psql("BEGIN READ ONLY; SELECT coalesce(json_agg(row_to_json(t)),'[]') FROM (SELECT row_to_json(a) asset,row_to_json(p) profile,row_to_json(f) file,coalesce((SELECT json_agg(row_to_json(h)) FROM hairstyle_style_tags h WHERE h.hairstyle_id=a.hairstyle_id),'[]'::json) tags FROM hairstyle_assets a JOIN files f ON f.file_id=a.thumbnail_file_id LEFT JOIN hairstyle_recommendation_profiles p USING(hairstyle_id) WHERE a.hairstyle_id IN ("+ids+") AND f.owner_type='SYSTEM' AND f.purpose='HAIRSTYLE_THUMBNAIL') t; COMMIT;") + rows = json.loads(snapshot) + assert len(rows) == 100, 'Reviewed original catalogue is missing records' + for r in rows: + expected = original[r['asset']['hairstyle_id']] + assert r['file']['file_id'] == expected['thumbnail_file_id'], 'Thumbnail changed since review' + assert r['file']['object_key'] == expected['object_key'], 'Object changed since review' + backup = Path.home() / 'heddy-catalog-backups' / data['release'] + backup.mkdir(parents=True, exist_ok=True,mode=0o700) + backup_file = backup/'before.json' + # Retain the very first snapshot across retries. + try: + with backup_file.open('x') as f: + os.chmod(backup_file,0o600) + f.write(snapshot) + except FileExistsError: + pass + for e in data['entries']: + content = (Path(bundle)/e['image_file']).read_bytes() + try: + head = client.head_object(Bucket=bucket,Key=e['object_key']) + except ClientError as ex: + if ex.response['Error']['Code'] not in {'404','NoSuchKey','NotFound'}: + raise + head = None + if head is None: + client.put_object(Bucket=bucket,Key=e['object_key'],Body=content,ContentType='image/jpeg', + Metadata={'sha256':e['sha256'],'source-photo-id':e['source_photo_id'],'catalog-release':data['release']},IfNoneMatch='*') + else: + assert head['Metadata'].get('sha256') == e['sha256'], 'Existing object content conflict' + # Verify actual S3 bytes before committing READY database records. + remote = client.get_object(Bucket=bucket,Key=e['object_key'])['Body'].read() + assert len(remote) == e['file_size'] and hashlib.sha256(remote).hexdigest() == e['sha256'] + print('Verified 50 uploaded catalogue images; applying atomic database change.',flush=True) + psql((Path(bundle)/'apply.sql').read_text()) + newids = ','.join("'"+e['hairstyle_id']+"'::uuid" for e in data['entries']) + result = psql("BEGIN READ ONLY; SELECT json_build_object('new_active',(SELECT count(*) FROM hairstyle_assets WHERE hairstyle_id IN ("+newids+") AND active),'all_active',(SELECT count(*) FROM hairstyle_assets WHERE active),'original_preserved',(SELECT count(*) FROM hairstyle_assets WHERE hairstyle_id IN ("+ids+"))); COMMIT;") + counts=json.loads(result) + assert counts['new_active']==50 and counts['original_preserved']==100 and counts['all_active']==92 + print(json.dumps(counts)) + + +if __name__ == '__main__': + parser=argparse.ArgumentParser() + parser.add_argument('bundle',type=Path) + parser.add_argument('--apply',action='store_true') + parser.add_argument('--fetch-images',action='store_true') + args=parser.parse_args() + try: + if args.fetch_images: fetch_images(args.bundle) + data=validate(args.bundle) + if args.apply: apply(args.bundle,data) + except Exception as error: + print(str(error),file=sys.stderr) + sys.exit(1) diff --git a/scripts/catalog_test.py b/scripts/catalog_test.py new file mode 100644 index 00000000..61337cb3 --- /dev/null +++ b/scripts/catalog_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Integration test against a disposable PostgreSQL database (never production).""" +import json +import os +from pathlib import Path +import subprocess +import sys +import uuid + +root = Path(__file__).resolve().parents[1] +bundle = Path(sys.argv[1]) +data = json.loads((bundle / 'manifest.json').read_text()) +dsn = os.environ['CATALOG_TEST_DATABASE_URL'] + +def sql(statement, succeeds=True): + result = subprocess.run(['psql', dsn, '-XqAt', '-v', 'ON_ERROR_STOP=1'], input=statement, text=True, capture_output=True) + assert (result.returncode == 0) == succeeds, result.stderr + return result.stdout.strip() + +def q(value): + return "'" + str(value).replace("'", "''") + "'" + +assert sql('SELECT count(*) FROM information_schema.tables WHERE table_schema=\'public\'') == '0', 'Test requires an empty disposable database' +for migration in sorted((root / 'src/main/resources/db/migration').glob('V*.sql'), key=lambda p:int(p.name.split('__')[0][1:])): + sql(migration.read_text()) +for original in data['original']: + sql("INSERT INTO files(file_id,upload_id,owner_type,purpose,status,object_key,content_type,file_size,expires_at,file_name) VALUES (" + ','.join([q(original['thumbnail_file_id']),q(uuid.uuid4()),"'SYSTEM'","'HAIRSTYLE_THUMBNAIL'","'READY'",q(original['object_key']),"'image/jpeg'",'100',"now()+interval '1 year'","'fixture.jpg'"]) + ');' + + 'INSERT INTO hairstyle_assets(hairstyle_id,style_name,category,thumbnail_file_id,active,asset_version) VALUES (' + ','.join([q(original['hairstyle_id']),q(original['style_name']),q(original['category']),q(original['thumbnail_file_id']),str(original['active']).lower(),"'fixture'"]) + ');' + + 'INSERT INTO hairstyle_recommendation_profiles(hairstyle_id,estimated_daily_care_minutes,management_difficulty,chemical_stress_level,metadata_version) VALUES (' + q(original['hairstyle_id']) + ",5,'LOW','LOW','fixture');") +apply_sql = (bundle/'apply.sql').read_text() +# A file identity conflict must roll back every insert/update in the transaction. +first = data['entries'][0] +sql("INSERT INTO files(file_id,upload_id,owner_type,purpose,status,object_key,content_type,file_size,expires_at,file_name) VALUES (" + ','.join([q(first['file_id']),q(uuid.uuid4()),"'SYSTEM'","'HAIRSTYLE_THUMBNAIL'","'READY'","'conflicting/key'","'image/jpeg'",'1',"now()+interval '1 year'","'conflict.jpg'"]) + ');') +sql(apply_sql, succeeds=False) +assert sql('SELECT count(*) FROM hairstyle_assets') == '100' +assert sql('SELECT count(*) FROM hairstyle_assets WHERE active') == '92' +sql('DELETE FROM files WHERE file_id='+q(first['file_id'])) + +def snapshot(): + return sql("SELECT json_build_object('assets',(SELECT json_agg(a ORDER BY hairstyle_id) FROM hairstyle_assets a),'profiles',(SELECT json_agg(p ORDER BY hairstyle_id) FROM hairstyle_recommendation_profiles p),'files',(SELECT json_agg(f ORDER BY file_id) FROM files f),'tags',(SELECT json_agg(json_build_array(hairstyle_id,style_tag_id) ORDER BY hairstyle_id,style_tag_id) FROM hairstyle_style_tags));") + +sql(apply_sql) +assert sql('SELECT count(*) FROM hairstyle_assets') == '150' +assert sql('SELECT count(*) FROM hairstyle_assets WHERE active') == '92' +retired = set(data['replacement_numbers'] + data['duplicate_numbers']) +for o in data['original']: + active = sql('SELECT active FROM hairstyle_assets WHERE hairstyle_id='+q(o['hairstyle_id'])) + assert active == ('f' if o['number'] in retired else str(o['active'])[0].lower()), o['number'] +for e in data['entries']: + actual = json.loads(sql('SELECT row_to_json(t) FROM (SELECT a.style_name,a.category,a.thumbnail_file_id,a.active,p.* FROM hairstyle_assets a JOIN hairstyle_recommendation_profiles p USING(hairstyle_id) WHERE a.hairstyle_id='+q(e['hairstyle_id'])+') t')) + for field in ['style_name','category','service_types','minimum_hair_length','compatible_hair_lengths','compatible_hair_types','estimated_daily_care_minutes','management_difficulty']: + assert actual[field] == e[field], (e['style_name'],field) + assert actual['active'] and actual['thumbnail_file_id'] == e['file_id'] + assert int(sql('SELECT count(*) FROM hairstyle_style_tags WHERE hairstyle_id='+q(e['hairstyle_id']))) == len(e['tags']) +first_state = snapshot() +sql(apply_sql) +assert snapshot() == first_state, 'Retry changed catalogue contents' +print('PASS: schema, 58 retirements, 50 replacements, 92 active, metadata/tags, atomic rollback and idempotent retry')