๐Ÿ’ก ํ”„๋ก ํŠธ์—”๋“œ

[React] ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ํ›„ ๋“ฑ๋ก ์ „ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋„์šฐ๊ธฐ

์˜ˆ์ง„-D 2023. 3. 9. 20:18

๋ฆฌ์•กํŠธ๋กœ ํ˜ผ์ž ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ๋„์ค‘ ์—ฌ๋Ÿฌ ํ…์ŠคํŠธ๋‚˜ ์ด๋ฏธ์ง€๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํผ์„ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ์ค‘ ์ด๋ฏธ์ง€๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ๊ณ ๋ฏผ์ด ์ƒ๊ฒผ์–ด์š”. ์•„๋ž˜๋Š” JSX์˜ ์ผ๋ถ€๋ฅผ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ธ๋ฐ, ์ด๋ฅผ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•ด๋ณด๋‹ˆ,

<div className="right_section">
	<p>์ด๋ฏธ์ง€๋Š” ํ•˜๋‚˜๋งŒ ๋“ฑ๋ก ๊ฐ€๋Šฅํ•ด์š”.</p>
	<input type="file" name="image" id="input_file" accept="image/*"/>
	<p>๋“ฑ๋ก๋œ ์ด๋ฏธ์ง€๋Š” ์ž๋™์œผ๋กœ ๋Œ€ํ‘œ์‚ฌ์ง„์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. :)</p>
</div>

์œ„ HTML ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ

์œ„์™€ ๊ฐ™์ด ํŒŒ์ผ ์„ ํƒ ๋ฒ„ํŠผ์ด ๋งค์šฐ ์•ˆ์˜ˆ์˜๊ฒŒ(...) ์ถœ๋ ฅ์ด ๋์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์ด๋ฅผ ๋ฐ”๊ฟ€ ๋ฐฉ๋ฒ•์ด ๋”ฑํžˆ ์—†๋‹ค๋Š” ๊ฒƒ์ด์—ˆ์ฃ . ์—…๋กœ๋“œ ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋„ ๋ฐ”๊พธ๊ณ  ์ด์™•์ด๋ฉด "์„ ํƒ๋œ ํŒŒ์ผ ์—†์Œ"์ด๋ผ๋Š” ํ…์ŠคํŠธ๋„ ์—†์• ๋ฒ„๋ฆฌ๊ณ  ์‹ถ์—ˆ์–ด์š”. ๐Ÿ˜‚

 

๋ฌธ์ œ ํ•ด๊ฒฐ: <label>์„ ์‚ฌ์šฉํ•˜์ž!โœจ

ํผ์—์„œ input ํƒœ๊ทธ์— ๋น ์ง์—†์ด ์‚ฌ์šฉํ•˜๋˜ <label>์„ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. <label> ํƒœ๊ทธ๋ฅผ ๊พธ๋ฏธ๊ณ  <input> ํƒœ๊ทธ๋ฅผ ์ˆจ๊ธฐ๋Š”๊ฑฐ์ฃ !

<div className="right_section">
	<p>์ด๋ฏธ์ง€๋Š” ํ•˜๋‚˜๋งŒ ๋“ฑ๋ก ๊ฐ€๋Šฅํ•ด์š”.</p>
	<label htmlFor="input_file">
		<div className="image_file">
			ํŒŒ์ผ ์—…๋กœ๋“œ
   		</div>
	</label>
	<input type="file" name="image" id="input_file" accept="image/*" />
	<p>๋“ฑ๋ก๋œ ์ด๋ฏธ์ง€๋Š” ์ž๋™์œผ๋กœ ๋Œ€ํ‘œ์‚ฌ์ง„์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. :)</p>
</div>

์ง ! css๋ฅผ ์‚ฌ์šฉํ•ด ์Šคํƒ€์ผ์„ <label>์— ์ ์šฉํ•ด์ฃผ๊ณ  ๋‚˜๋‹ˆ ์ด๋ ‡๊ฒŒ ์˜ˆ์œ ์—…๋กœ๋“œ ๋ฐ•์Šค๊ฐ€ ์™„์„ฑ๋˜์—ˆ์–ด์š”. ์ด์ œ ์ € ๋ฐ•์Šค ์ „์—ญ์„ ํด๋ฆญํ•˜๋ฉด ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•˜๊ธฐ๋„ ์‰ฌ์›Œ์ง€๊ณ  ๋ฏธ๊ด€๋„ ์ข‹์•„์กŒ์–ด์š”.

 

์ด๋ฏธ์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ตฌํ˜„ํ•˜๊ธฐ

๊ทธ๋Ÿฌ๋‚˜ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด๋„ ์—…๋กœ๋“œ๊ฐ€ ์ž˜๋๋Š”์ง€, ์–ด๋–ค ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋๋Š”์ง€ ์ „ํ˜€ ์•Œ ์ˆ˜๊ฐ€ ์—†์–ด์š”. ์ด๋Ÿฐ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๊ฐ€ ๋ถˆํŽธํ•จ์„ ๋Š๋‚„๊ฒŒ ๋ถ„๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋˜๋ฉด ์ € ๋ฐ•์Šค ๋‚ด์— ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ ์ฑ„์šฐ๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์ € ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ์•ž์„œ ์ž‘์„ฑํ–ˆ๋˜ <label> ์•ˆ์— ์ด๋ฏธ์ง€๋ฅผ ๋‹ด์„ ์˜์—ญ์„ ์ถ”๊ฐ€ํ•ด๋‘๊ณ , ์ด๋ฅผ display: none์œผ๋กœ ์ˆจ๊ฒจ๋‘ก๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋˜๋ฉด useRef()๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฏธ์ง€๋ฅผ src ์†์„ฑ์— ๋„ฃ๊ณ , ํ•ด๋‹น ์˜์—ญ์ด ๋ณด์ด๋„๋ก ์Šคํƒ€์ผ๋งํ–ˆ์Šต๋‹ˆ๋‹ค.

 

<div className="right_section">
	<p>์ด๋ฏธ์ง€๋Š” ํ•˜๋‚˜๋งŒ ๋“ฑ๋ก ๊ฐ€๋Šฅํ•ด์š”.</p>
	<label htmlFor="input_file">
		<div ref={image_input} className="image_file"> <!-- ref ์ถ”๊ฐ€ -->
			ํŒŒ์ผ ์—…๋กœ๋“œ
   		</div>
        	<!-- ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ์˜์—ญ -->
    		<div ref={image_preview} className="image_file preview"> <!-- ref ์ถ”๊ฐ€ -->
    			<img src=""></img>
		</div>
	</label>
    	<!-- onChange ์ถ”๊ฐ€ -->
	<input type="file" name="image" id="input_file" accept="image/*" onChange={handleChange}/>
	<p>๋“ฑ๋ก๋œ ์ด๋ฏธ์ง€๋Š” ์ž๋™์œผ๋กœ ๋Œ€ํ‘œ์‚ฌ์ง„์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. :)</p>
</div>
.image_file.preview {
	display: none;
}

์œ„์™€ ๊ฐ™์ด ์˜์—ญ์„ ์ถ”๊ฐ€ํ•œ ํ›„, ์ผ๋‹จ์€ display: none์œผ๋กœ ๊ฐ€๋ ค๋’€์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ JSX์— ์ถ”๊ฐ€๋œ ์ฝ”๋“œ๋“ค์ด ๋” ์žˆ์–ด์š”. ๋ฐ”๋กœ ref์™€ input์˜ onChange์ธ๋ฐ์š”, ์ด์— ๋Œ€ํ•œ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

const image_preview = useRef();
const image_input = useRef();

function handleChange(e) {
        const { name, value } = e.target;
        if (name === 'image') {
            const imgURL = URL.createObjectURL(e.target.files[0]);
            image_input.current.setAttribute('style', 'display: none;');
            image_preview.current.setAttribute('style', 'display: flex');
            image_preview.current.children[0].setAttribute('src', imgURL);
        }
}

๋จผ์ € image_preview๋Š” ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋˜๋ฉด ์ด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณด์—ฌ์ค„ ์˜์—ญ์ด๊ณ , image_input์€ ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋˜๋ฉด ๊ฐ์ถฐ์ค„ ์˜์—ญ์ด์—์š”. ๊ทธ๋ฆฌ๊ณ  handleChange ํ•จ์ˆ˜๋Š” ํ•ด๋‹น ํƒ€๊ฒŸ์ด ๋ณ€ํ•˜๋ฉด ๊ทธ์˜ name๊ณผ value๋ฅผ ๋ฐ›์•„ ๋ชจ๋“  ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ €๋Š” hadleChange ํ•จ์ˆ˜์—์„œ setState๋„ ํ•จ๊ป˜ ์ง„ํ–‰ํ–ˆ์ง€๋งŒ ์ง€๊ธˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด๋ฏธ์ง€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ด€๋ จ ์ฝ”๋“œ๋งŒ ์ง‘์ค‘์ ์œผ๋กœ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด ์ œ์™ธํ–ˆ์–ด์š”. 

const imgURL = URL.createObjectURL(e.target.files[0]);

e.target.files[0]์€ ์šฐ๋ฆฌ๊ฐ€ <input>์—์„œ ๋ณด๋‚ด์˜จ ์ด๋ฏธ์ง€ Blob ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์ด Blob ๊ฐ์ฒด๋ฅผ url๋กœ ๋ฐ”๊พธ์–ด src ์†์„ฑ์œผ๋กœ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด URL.createObjectURL() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. (์ฃผ์˜: ์ด url์€ window ์ฐฝ์„ ๋‹ซ๋Š” ์ฆ‰์‹œ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.)

image_input.current.setAttribute('style', 'display: none;');
image_preview.current.setAttribute('style', 'display: flex');
image_preview.current.children[0].setAttribute('src', imgURL);

url๋กœ ๋ฐ”๊ฟจ๋‹ค๋ฉด ์ด์ œ ์ ์šฉ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์•ž์„œ useRef()๋กœ ์ง€์ •ํ•ด๋’€๋˜ image_input๊ณผ image_preview๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € ์ด๋ฏธ์ง€ ์ž…๋ ฅ์„ ์œ ๋„ํ–ˆ๋˜ ์˜์—ญ์„ display: none์œผ๋กœ ์ˆจ๊ฒจ์ฃผ๊ณ ,

์ด๋ฏธ์ง€๋ฅผ ๋„ฃ์„ ์˜์—ญ์„ display: flex๋กœ ๋ณด์—ฌ์ค„๊ฒŒ์š”.

๋งˆ์ง€๋ง‰์œผ๋กœ image_preview์˜ ์ž์‹ ํƒœ๊ทธ์ธ <img>๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ .children์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. img๋Š” ์ฒซ๋ฒˆ์งธ ์ž์‹์ด๋‹ˆ .children[0]์„ ์‚ฌ์šฉํ•ด์คฌ์–ด์š”. ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ณ€ํ™˜ํ•œ url์„ src ์†์„ฑ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

div.preview {
	overflow: hidden
}
div.preview img {
	height: 100%;
}

๋์œผ๋กœ ์œ„์™€ ๊ฐ™์ด ์ถ”๊ฐ€๋œ ์ด๋ฏธ์ง€๊ฐ€ ๋ฐ•์Šค๋ฅผ ๊ฝ‰ ์ฑ„์šธ ์ˆ˜ ์žˆ๋„๋ก ์Šคํƒ€์ผ ์ง€์ •๊นŒ์ง€ ํ•ด์ฃผ๋ฉด ์™„์„ฑ์ž…๋‹ˆ๋‹ค!

 

๊ฒฐ๊ณผ

์™„์„ฑ!

์˜ˆ์˜๊ฒŒ ์ด๋ฏธ์ง€๊ฐ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋กœ ๋“ฑ๋ก๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :)

๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ๊ฒƒ ๊ฐ™์ง€๋งŒ ์ผ๋‹จ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค๋Š”๊ฒŒ ๋ฟŒ๋“ฏํ•˜๋„ค์š”. ๐Ÿฅฐ