๊ฒฌ์ฃผ(์ ์ )๊ฐ ๊ฐ์ ์ ์ฒญ ์น์ธ ์ดํ ํ๋กํ ์ค์ ํ์ด์ง๋ก ๋์ด๊ฐ๊ฒ ๋๋ค.
๋ด๊ฐ ๊ธฐ๋ํ๋ ์ก์ ํ๋ก์ฐ
- ์ด๋ฏธ์ง ์ธ๋ค์ผ์ด ํ์๋๋ ์์ญ ํด๋ฆญ
- ๋ฒํผ UI ํ์ฑํ (active ์ํ)
- ๋ฒํผ UI ํ์ฑํ ์ํ์์ ๋ค์ ํด๋ฆญ ์ ์ด๋ฏธ์ง ์ ๋ก๋ ์ฐฝ ๋์์ง๊ธฐ
์ด๋ฏธ์ง ์ ๋ก๋ ์ | ์ด๋ฏธ์ง ์ ํ ์ |
---|---|
![]() | ![]() |
ios ๋ชจ๋ฐ์ผ ํ๊ฒฝ์์ active UI๋ ํ์ ๋๋๋ฐ ์ด๋ฅผ ํด๋ฆญ ํ ๊ฒฝ์ฐ ์ด๋ฏธ์ง ์
๋ก๋ ์ฐฝ์ด ๋์์ง์ง ์์๋ค.
ํด๋น ํ์ด์ง๋ ๊ฐ์
์ ์ฒญ ์ ์ฐจ์์ ๋ฌด์กฐ๊ฑด ์งํ ๋์ด์ผ ํ๋ ํ๋ก์ฐ๋ก ์ด์๋ฅผ ํ์ธ ํ๊ณ ๋น ๋ฅด๊ฒ ํด๊ฒฐํ์ด์ผ ํ๋ค.
๋ค๋ง ๋ค๋ฅธ ํ๊ฒฝ์์ ์ ๋๋๋ฐ ios ๋ชจ๋ฐ์ผ ํ๊ฒฝ์์ ์ ์ฉ์ด ์ ๋๊ณ ์๋ ์ํฉ์ด์๋ค.
์๋๋ผ๋ฉด button์ด focus๋๋ฉด active UI๊ฐ ํ์๋๊ณ ๋ค๋ฅธ ๊ณณ ํฐ์น(onBlur) ํ ๊ฒฝ์ฐ ํด๋น active UI๊ฐ ํ๋ ค์ผ ํ๋ค.
ios ๋ชจ๋ฐ์ผ ํ๊ฒฝ์์ onBlur๊ฐ ์ ๋๋ก ์ ์ฉ๋์ง ์๋ ์ด์๊ฐ ์์๋ค.
iosํ๊ฒฝ์์ onBlur ์ด์ ๊ด๋ จ ๋ ํผ๋ฐ์ค
https://stackoverflow.com/questions/61245883/why-blur-and-focus-doesnt-work-on-safari
๊ตฌ๊ธ๋ง ํด๋ณด๋ ios ๋ชจ๋ฐ์ผ ํ๊ฒฝ์์๋ ๋ค๋ฅธ ์์ญ์ ํฐ์นํด๋ focus๋ ์์ง ์ด์ ์์ญ์ ๋จธ๋ฌผ๋ฌ ์์ด blur๊ฐ ์ ๋๋ก ๋์ํ์ง ์์ ์๋ ์๋ค๊ณ ํ๋ค.
ํ๋กํ ์ค์ ์ ProfileUploadBox
์ปดํฌ๋ํธ์์ ๊ด๋ฆฌ๋๊ณ ์๋ค.
ํ๋กํ ์์ , ๋ฑ๋ก์ผ๋ก ๋๋๊ฒ ๋์ง๋ง ๋์ผํ ๊ธฐ๋ฅ์ ํ๊ณ ์์ด ๊ด๋ฆฌํ๊ธฐ ํธ๋ฆฌํ๋๋ก ๋ฌถ์ด ๋์๋ค.
ํ์ฌ ๋ค์ํ ํ๊ฒฝ์ ์๊ฐํ์ง ์๊ณ ๊ตฌ์กฐ๋ฅผ ์์ฑ + ๋ถํ์ํ ์ฝ๋๊ฐ ๋ง์ ์ฝ๋ ๊ฐ์ ์ด ํ์ํ๋ค.
// ProfileUploadBox.tsx ์์ ์
type Mode = "create" | "edit";
interface ProfileUploadProps {
type: string;
isActive?: boolean;
setIsActive?: React.Dispatch<React.SetStateAction<boolean>>;
fileRef?: React.RefObject<HTMLInputElement>;
fileName: string;
mode: Mode;
onClick?: () => void;
}
const ProfileUploadBox = ({
type,
isActive,
setIsActive,
fileRef,
fileName,
mode,
onClick
}: ProfileUploadProps) => {
const { setValue } = useFormContext();
const [profile, setProfile] = useState<IFile[]>([]);
const handleClick = () => {
if (fileRef && fileRef.current) {
mode === "create" && isActive ? setIsActive && setIsActive(false) : fileRef.current.click();
}
onClick && onClick();
};
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
const FileList = e.target.files;
if (!FileList) {
showToast("์
๋ก๋ํ ํ์ผ์ด ์์ต๋๋ค.", "ownerNav");
return;
}
// ํ์ผ ๋ณ๊ฒฝ ์์ ๊ฒฝ์ฐ
if (FileList.length <= 0) {
if (!isActive && setIsActive) setIsActive(true);
return;
}
if (FileList) {
const newFiles = Array.from(FileList);
const fileArray = await Promise.all(newFiles.map(getFilePreview));
setProfile([...fileArray]);
setValue(fileName, [...newFiles], { shouldValidate: true });
if (mode === "create" && setIsActive) setIsActive(true);
}
};
return (
<>
{mode === "create" && (
<ProfileCreate
isActive={isActive}
setIsActive={setIsActive}
profile={profile}
fileInputRef={fileRef}
handleFileChange={handleFileChange}
handleClick={handleClick}
registerText={fileName}
type={type}
/>
)}
{mode === "edit" && (
<ProfileEdit
profile={profile}
handleFileChange={handleFileChange}
handleClick={handleClick}
registerText={fileName}
type={type}
/>
)}
</>
);
};
export default ProfileUploadBox;
// ProfileCreate ์์ ์
const ProfileCreate = ({
isActive,
setIsActive,
profile,
fileInputRef,
handleFileChange,
registerText,
type
}: ProfileCreateProps) => {
const divRef = useRef<HTMLButtonElement>(null);
const { register } = useFormContext();
const handleClickTarget = () => {
divRef?.current?.focus();
};
const handleActive = () => {
isActive && setIsActive?.(false);
};
return (
<Flex align="center" direction="column" justify="center" gap="12" width="100%">
<S.ProfileBox>
<S.UploadProfileButton
id={type}
onClick={handleClickTarget}
onBlur={() => setIsActive?.(true)}
ref={divRef}
aria-label="uploadProfileButton"
>
{profile.length > 0 ? (
<>
<S.UploadImage
onClick={handleActive}
src={profile[0].thumbnail}
alt={`${type}-profile`}
/>
{!isActive && <ProfileActiveBox htmlFor={registerText} />}
</>
) : (
<>
<S.ProfileLabel htmlFor={registerText} />
<AddCIcon />
</>
)}
</S.UploadProfileButton>
</S.ProfileBox>
<S.StyledHiddenUpload
{...register(registerText, {
required: true,
onChange: (e) => handleFileChange(e, type)
})}
ref={fileInputRef}
id={registerText}
type="file"
accept={ACCEPT_FILE_TYPE.IMAGE}
/>
</Flex>
);
};
ํด๋น ์ด์๋ PC ํ๊ฒฝ์ด ์๋ Mobile ํ๊ฒฝ์ผ๋ก ์น์์ ํ์ธํ ๋ฐฉ๋ฒ์ด ์์๋ค.
๋งฅ๋ถ์ Xcode ํ๋ก๊ทธ๋จ์ ํตํด ํ์ธ ํ๋ ๋ฐฉ๋ฒ๋ ์์ง๋ง
๋์ ๋
ธํธ๋ถ์ ๊ฑฐ์ 6๋
์ด ๋์ด์ ๊ทธ๋ฐ์ง... Xcode ์๋ฎฌ๋ ์ดํฐ ๋ฒํฐ์ง ๋ชปํ๋ค..ใ
๊ทธ๋ฌ๋ค ํด๋ํฐ์์๋ local ํ๊ฒฝ์ ํ์ธ ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์์ ๋๊ณ
์ฝ๋ ์์ ์ ์ค์๊ฐ์ผ๋ก ๋ฐ์๋์ด ๋์ค์๋ ๋ชจ๋ฐ์ผ ํ๊ฒฝ ์์
์ ํ ๋๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ๊ฒ ๊ฐ๋ค.
์์ดํฐ์์ local ํ๊ฒฝ ๋์ฐ๊ธฐ
- ๋งฅ๋ถ, ์์ดํฐ์ด ๋์ผํ ์์ดํ์ด๋ฅผ ์ฌ์ฉํ๊ณ ์๋์ง ํ์ธ
- ๋งฅ๋ถ์ ์ฐ๊ฒฐ๋์ด ์๋ ์์ดํ์ด ์์ดํผ ์ฃผ์ ํ์ธ
์์ดํผ์ฃผ์:3000
์ ์์ดํฐ ์ฌํ๋ฆฌ ์ฃผ์์ฐฝ์ ์ ๋ ฅ
button์ onBlur={() => setIsActive?.(true)}
๊ฐ ์ ํํ ๋์ํ์ง ์์ ProfileActiveBox
๊ฐ ํ์ฑํ ๋์ด๋ file upload ํ๋ฉด์ ๋์ฐ์ง ์๋ ์ ์ด์๋ค.
onBlur๋ก ํ๋ ์ด์ ๋ ์ต๋ํ ํจ์๋ฅผ ์์ฑํ์ง ์๊ณ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ผ๋ก ์์ ํ์๋ค.
์ฐ์ isActive
์ธ boolean state์ onBlur
๋ก ๊ด๋ฆฌํ๋ ์ฝ๋ ๋ถํฐ ์์ ํ๋ค.
isActive๋ฅผ ํตํด ProfileActiveBox
๋ฅผ ํ์ฑํ ๋๋๋ก ํ๋ ๋ถ๋ถ์
ํ์ฌ ํด๋ฆญํ(active๋) ๋ฐ์ดํฐ๊ฐ ํด๋น type๊ณผ ์ผ์นํ ๊ฒฝ์ฐ boolean ๊ฐ์ผ๋ก ํ์ํ์ฌ ๋ฒํผ ํด๋ฆญ ์ ํด๋น ์์ญ๋ง ํ์ฑํ(true) ๋๋๋ก ์ ๋ฌํ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝ ํ๋ค.
// ๋ฐ์ดํฐ ๋๊ฒจ ์ค ๋ (์์ ์ )
const [isMyActive, setIsMyActive] = useState(false);
const [isDogActive, setIsDogActive] = useState(false);
const myFileInputRef = useRef<HTMLInputElement>(null);
const dogFileInputRef = useRef<HTMLInputElement>(null);
const profileDatas = [
{
type: TYPE_NAME.MEMBER,
isActive: isMyActive,
setIsActive: setIsMyActive,
fileName: FILE_NAME.PROFILE_MEMBER,
fileRef: myFileInputRef
},
{
type: TYPE_NAME.DOG,
isActive: isDogActive,
setIsActive: setIsDogActive,
fileName: FILE_NAME.PROFILE_DOG,
fileRef: dogFileInputRef
}
];
// ๋ฐ์ดํฐ ๋๊ฒจ ์ค ๋ (์์ ํ)
const [activeType, setActiveType] = useState("");
const myFileInputRef = useRef<HTMLInputElement>(null);
const dogFileInputRef = useRef<HTMLInputElement>(null);
const profileDatas = [
{
type: TYPE_NAME.MEMBER,
isType: activeType === TYPE_NAME.MEMBER,
fileName: FILE_NAME.PROFILE_MEMBER,
fileRef: myFileInputRef
},
{
type: TYPE_NAME.DOG,
isType: activeType === TYPE_NAME.DOG,
fileName: FILE_NAME.PROFILE_DOG,
fileRef: dogFileInputRef
}
];
๋ํ ๊ธฐ์กด์ onBlur๋ฅผ ์ฌ์ฉํ๋ ๋ฒํผ์ ์์ฑ์ ์ ๋ถ ์ง์ฐ๊ณ
์ค์ ๋ก ํด๋ฆญํด์ผ ํ๋ ์์ญ์ onClicke ์ด๋ฒคํธ๋ฅผ ์ถ๊ฐํด
๋ง์ฝ true์ธ ๊ฒฝ์ฐ ProfileActiveBox
๊ฐ ํ์ฑํ ๋๋๋ก ์์ ํ๋ค.
const handleClickTarget = (currentType: string) => {
setActiveType?.(currentType);
};
return (
<Flex align="center" direction="column" justify="center" gap="12" width="100%">
<S.ProfileBox id={type}>
<S.UploadProfileButton aria-label="uploadProfileButton">
{profile.length > 0 ? (
<>
{isActiveType && (
<S.ProfileLabel htmlFor={registerText} ref={labelRef}>
<ProfileActiveBox />
</S.ProfileLabel>
)}
<S.UploadImage
onClick={() => handleClickTarget(type)}
src={profile[0].thumbnail}
alt={`${type}-profile`}
/>
</>
) : (
<>
<S.ProfileLabel htmlFor={registerText} />
<AddCIcon />
</>
)}
</S.UploadProfileButton>
</S.ProfileBox>
<S.StyledHiddenUpload
{...register(registerText, {
required: true,
onChange: (e) => {
handleFileChange(e, type);
}
})}
ref={fileInputRef}
id={registerText}
type="file"
accept={ACCEPT_FILE_TYPE.IMAGE}
/>
</Flex>
);
์ต๋ํ ํ๊ทธ ๋ณธ์ฐ์ ๊ธฐ๋ฅ์ ์ด๋ฆฌ๊ณ ์ถ์๋ค.
label
ํ๊ทธ ์์ฑ์ htmlFor
๊ณผ input
ํ๊ทธ ์์ฑ์ id
๊ฐ์ ๋์ผํ๊ฒ ํด๋ ํด๋น ์์ญ ํด๋ฆญ ์ ํ์ฑํ ๋๋ค.
๊ตณ์ด onClick ์ด๋ฒคํธ์ ํจ์๋ฅผ ์ถ๊ฐํ ํ์๊ฐ ์๊ธฐ์ label ํ๊ทธ๋ก ํด๊ฒฐํ๋ค..
ios ํ๊ฒฝ์์ mousedown์ด focus์ ๋น์ทํ๊ฒ ์์ฉ ๋๋ค๊ณ ํ๋ค.
const labelRef = useRef<HTMLLabelElement>(null);
const handleClickOutside = useCallback(
(e: MouseEvent) => {
const labelTarget = labelRef?.current;
const inside = labelTarget?.contains(e.target as Node);
if (inside) return;
// ์ธ๋ถ ํด๋ฆญ ์ ๋ชจ๋ ํ์ฑํ ํด์
if (labelTarget && !inside) {
setActiveType?.("");
}
},
[setActiveType]
);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [handleClickOutside, setActiveType]);
๋ง์ฝ labelTarget ๋ง๊ณ ๋ค๋ฅธ ์์ญ ํด๋ฆญ ์ setActiveType์ ๋น ๋ฌธ์์ด์ ์ฃผ์ด ๋นํ์ฑํ ๋๋๋ก ํ๋ค.
ํ์ผ ์ ๋ก๋ ์ "์ทจ์" ๋ฒํผ ํด๋ฆญ ํ ๊ฒฝ์ฐ ๊ณ์ active ์ํ๊ฐ ๋์ด ์์๋ค.
์ด๋ focus
์ด๋ฒคํธ๋ฅผ ํ์ฉํ๋ฉด ๋๋ค๊ณ ํ๋ค.
์๋ง ์ทจ์ ๋ฒํผ ํด๋ฆญ ์ ํฌ์ปค์ค๊ฐ ํด๋น ํ์ด์ง(window)๋ก ์ด์ ์ด ๋ง์ถฐ์ง๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ์ง ์๋ ์ถ์ธกํด ๋ณธ๋ค.
๋ค๋ง ios ํ๊ฒฝ์์๋ focus ์ด๋ฒคํธ๊ฐ ์ ๋๋ก ๋์ํ๊ณ ์์ง ์์์ ์ด์ ์ ๋ค์ ํ์ธ์ด ํ์ํ ๊ฒ ๊ฐ๋ค...ใ
// ProfileUploadBox.tsx
const handleClick = () => {
if (fileRef && fileRef.current) {
mode === "create" && isActiveType ? setActiveType?.("") : fileRef.current.click();
}
onClick && onClick();
};
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
const FileList = e.target.files;
if (!FileList) {
showToast("์
๋ก๋ํ ํ์ผ์ด ์์ต๋๋ค.", "bottom");
return;
}
// ํ์ผ ์ ํ ํ "์ทจ์" ํด๋ฆญ ์
if (FileList.length === 0) {
setActiveType?.("");
return;
}
if (FileList) {
const newFiles = Array.from(FileList);
const fileArray = await Promise.all(newFiles.map(getFilePreview));
setProfile([...fileArray]);
setValue(fileName, [...newFiles], { shouldValidate: true });
if (mode === "create") setActiveType?.("");
}
};
useEffect(() => {
// FIXME ๋ชจ๋ฐ์ผ ํ๊ฒฝ focus ๋์ฒด ์ด๋ฒคํธ ํ์ธ ํ ์์ ํ์
// ํ์ผ ๋ฏธ์ ํ ํ "์ทจ์" ํด๋ฆญ ์
window.addEventListener("focus", () => setActiveType?.(""));
return () => {
window.removeEventListener("focus", () => setActiveType?.(""));
};
}, [setActiveType]);
// ProfileUploadBox.tsx
const ProfileUploadBox = ({
type,
fileRef,
fileName,
mode,
onClick,
isActiveType,
setActiveType
}: ProfileUploadProps) => {
const { setValue } = useFormContext();
const [profile, setProfile] = useState<FileType[]>([]);
const handleClick = () => {
if (fileRef && fileRef.current) {
mode === "create" && isActiveType ? setActiveType?.("") : fileRef.current.click();
}
onClick && onClick();
};
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
const FileList = e.target.files;
if (!FileList) {
showToast("์
๋ก๋ํ ํ์ผ์ด ์์ต๋๋ค.", "bottom");
return;
}
// ํ์ผ ์ ํ ํ "์ทจ์" ํด๋ฆญ ์
if (FileList.length === 0) {
setActiveType?.("");
return;
}
if (FileList) {
const newFiles = Array.from(FileList);
const fileArray = await Promise.all(newFiles.map(getFilePreview));
setProfile([...fileArray]);
setValue(fileName, [...newFiles], { shouldValidate: true });
if (mode === "create") setActiveType?.("");
}
};
useEffect(() => {
// FIXME ๋ชจ๋ฐ์ผ ํ๊ฒฝ focus ๋์ฑ ์ด๋ฒคํธ ํ์ธ ํ ์์ ํ์
// ํ์ผ ๋ฏธ์ ํ ํ "์ทจ์" ํด๋ฆญ ์
window.addEventListener("focus", () => setActiveType?.(""));
return () => {
window.removeEventListener("focus", () => setActiveType?.(""));
};
}, [setActiveType]);
return (
<>
{mode === "create" && (
<ProfileCreate
profile={profile}
fileInputRef={fileRef}
registerText={fileName}
type={type}
isActiveType={isActiveType}
setActiveType={setActiveType}
handleFileChange={handleFileChange}
handleClick={handleClick}
/>
)}
{mode === "edit" && (
<ProfileEdit
profile={profile}
registerText={fileName}
type={type}
handleFileChange={handleFileChange}
handleClick={handleClick}
/>
)}
</>
);
};
export default ProfileUploadBox;
// ProfileCreate.tsx
const ProfileCreate = ({
profile,
fileInputRef,
registerText,
type,
isActiveType,
setActiveType,
handleFileChange
}: ProfileCreateProps) => {
const labelRef = useRef<HTMLLabelElement>(null);
const { register } = useFormContext();
const handleClickTarget = (currentType: string) => {
setActiveType?.(currentType);
};
const handleClickOutside = useCallback(
(e: MouseEvent) => {
const labelTarget = labelRef?.current;
const inside = labelTarget?.contains(e.target as Node);
if (inside) return;
// ์ธ๋ถ ํด๋ฆญ ์ ๋ชจ๋ ํ์ฑํ ํด์
if (labelTarget && !inside) {
setActiveType?.("");
}
},
[setActiveType]
);
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [handleClickOutside, setActiveType]);
return (
<Flex align="center" direction="column" justify="center" gap="12" width="100%">
<S.ProfileBox id={type}>
<S.UploadProfileButton aria-label="uploadProfileButton">
{profile.length > 0 ? (
<>
{isActiveType && (
<S.ProfileLabel htmlFor={registerText} ref={labelRef}>
<ProfileActiveBox />
</S.ProfileLabel>
)}
<S.UploadImage
onClick={() => handleClickTarget(type)}
src={profile[0].thumbnail}
alt={`${type}-profile`}
/>
</>
) : (
<>
<S.ProfileLabel htmlFor={registerText} />
<AddCIcon />
</>
)}
</S.UploadProfileButton>
</S.ProfileBox>
<S.StyledHiddenUpload
{...register(registerText, {
required: true,
onChange: (e) => {
handleFileChange(e, type);
}
})}
ref={fileInputRef}
id={registerText}
type="file"
accept={ACCEPT_FILE_TYPE.IMAGE}
/>
</Flex>
);
};
export default ProfileCreate;
์ด์ ์ฝ๋์ ๋น๊ต ํ์ ๋ ์ฝ๋๊ฐ ๊ฐ์ ๋๊ฑธ ํ์ธํ ์ ์์๋ค.
๋ฌด์ ๋ณด๋ค ์ฌ์ฉ์ ์
์ฅ์ผ๋ก ์๊ฐํด ์ฌ๋ฌ ๋ณ์์ QA๋ฅผ ์งํํ๋ฉฐ ์์ธ ์ฒ๋ฆฌ๋ ๊ฐ์ด ์์
ํ๋ค.
์์ฌ์ด ์ ์ ios ํ๊ฒฝ์์ ์ด๋ฏธ์ง ๋ฏธ์ ํ ์ํ๋ก "์ทจ์" ๋ฒํผ ํด๋ฆญ ์ focus ๋นํ์ฑํ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผํ ์ง,
๊ทธ๋ฆฌ๊ณ props๋ฅผ 2๋ฒ ๋ด๋ ค ๋ฐ๋ ๊ตฌ์กฐ์ธ๋ฐ ์ด๊ฒ ๋ง๋์ง ๊ณ ๋ฏผ์ด๋ค.
๋ํ useForm์ ref๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๊ณ ์์ด์
๋ฐ์ดํฐ ์ ๋ฌ ์ ref ๋ฐ์ดํฐ๊ฐ ๊ตณ์ด ์์ด์ผ ํ๋ ์ถ๊ธฐ๋ ํ๋ค.
ํญ์ ์ฌ์ฌ์ฉ์ฑ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ๋
ํด๋น ๊ธฐ๋ฅ์ด 2๋ฒ ์ด์ ๋์ผํ ๊ธฐ๋ฅ์ผ๋ก ์ฌ์ฉ๋๊ณ ์๋์ง?๋ฅผ ์๊ฐํ๋ฉด์ ์์
ํ๊ณ ์์ง๋ง...
๊ตฌ์กฐ์ ๋ํด์ ์ข ๋ ๊น๊ฒ ๊ณ ๋ฏผํด์ผ ๋์ค์ ์ ์ง๋ณด์ ์ธก๋ฉด์์๋ ์ข์ง ์์๊น ์ถ๋ค.
์ง๊ธ์ ๊ธํด์ ์ด์ ์ฌํญ๋ง ํด๊ฒฐ ํ๋๋ฐ ๋์ค์ ๋ฆฌํฉํ ๋ง ์์ ์ ํด์ผ๊ฒ ๋ค.
์ด์๋ฅผ ํด๊ฒฐํ๊ณ ์ด๋ฅผ ๋ธ๋ก๊ทธ๋ก ์์ฑํ๋ ค๊ณ ํ๋ ์ ๋๋ก ์ ๋ฆฌ๊ฐ ์ ๋๋ ๊ธฐ๋ถ์ด๋ค...
ํผ์ ๋ณผ ๋ ์ดํด๊ฐ ๋์ง๋ง ๋ค๋ฅธ ์ฌ๋์ด ํด๋น ๊ฒ์๊ธ์ ๋ณด๋ฉด ์ดํด๋ฅผ ๋ชป ํ์ค ๊ฒ ๊ฐ๋ค.
๋ค๋ฅธ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ๊ณ ํด ๊ฒ์๊ธ ๋ด์ฉ์ ๋ค๋ฅธ ๋ถ๋ค์ด ๋ดค์ ๋ ๋์์ด ๋๊ณ ์ดํด ๋๋๋ก ์กฐ๊ธ์ฉ ์์ ํด ๋ณด์.