웹/프론트엔드

[React] 이미지 모달 구현하기

이민훈 2021. 10. 21. 18:02

https://hackids.tistory.com/127

 

[React] React로 만든 이력서 Github Pages로 배포 및 후기

서론 학교를 졸업하고 취업을 준비한 지도 8개월이 지났습니다. 부족했던 자료구조와 알고리즘 실력을 키우기 위해 백준과 같은 온라인 저지에서 문제도 많이 풀어보고, 종만북이라 불리는 알

hackids.tistory.com

 

이력서를 리액트로 개발할 때, 이미지 모달 부분이 조금 힘들었던 기억이 있습니다.

 

라이브러리를 쓰지 않고 구현해보려 노력했고, 기본적인 기능은 나름 갖춘 모달을 완성하였습니다.

 

이미지 모달이 실행되는 페이지

 

이미지 모달

 

리액트와 css만을 이용해서 이미지 모달 구현하는 법을 알아보겠습니다.

 

App.js

import { Component } from "react";
import Modal from "./Modal";

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            contents: ['image1.jpg', 'image2.jpg', 'image3.jpg'],
            index: null,
            hasModal: false,
        };
    }

    controlModal = (index) => {
        this.setState({
            index: index,
            hasModal: !this.state.hasModal
        });
    }

    render() {
        const images = this.state.contents.map((image, index) =>
            <img key={index} onClick={() => this.controlModal(index)} src={image} alt="alt" />
        )

        return (
            <div>
                {images}
                {this.state.hasModal && (
                    <Modal images={this.state.contents} index={this.state.index} close={this.controlModal}></Modal>
                )}
            </div>
        );
    }
}

export default App;

 

먼저 App.js 파일입니다.

 

이미지 파일들(contents), 현재 몇 번째 이미지가 클릭 되어있는지(index), 모달이 활성화된 상태인지(hasModal)

 

3개의 state를 관리해주겠습니다.

 

controlModal 함수는 index와 hasModal의 값을 바꿔주는 함수입니다.

 

contents의 각 이미지에는 controlModal 함수가 정의되어 있고 파라미터로는 index를 넘깁니다.

 

렌더링 되는 부분을 보시면 this.state.hasModal && ~ 부분이 있는데,

 

state의 hasModal 값에 따라 Modal 컴포넌트를 렌더링하겠다 라는 의미로,

 

true면 Modal 창이 렌더링 되고 false면 렌더링 되지 않는다고 보시면 될 것 같습니다.

 

Modal 컴포넌트에는 contents와 index, 그리고 controlModal 함수를 props로 넘겨주겠습니다.

 

Modal.js

import { Component } from 'react';
import "./styles.css";

class Modal extends Component {
    constructor(props) {
        super(props);
        this.state = {
            index: this.props.index
        };
    }

    imageChange = (index) => {
        this.setState({
            index: index
        });
    }

    render() {
        return (
            <div className="background">
                <div className="modal"></div>
            </div>
        );
    }
}

export default Modal;

모달 컴포넌트입니다.

 

props로 넘겨받은 index를 state의 초깃값으로 설정해줍니다.

 

imageChange라는 함수는 index를 파라미터로 넘겨받아 값을 바꿔주는 함수입니다.

 

styles.css

.background {
    position: fixed;
    display: flex;
    align-items: center;
    justify-content: center;
    top:0;
    left:0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
}

.modal {
    background-color: white;
    width: 80%;
    height: 90%;
}

 

css 파일입니다.

 

background는 모달창뒤로 보이는 화면들을 약간 어둡게 하여 모달창에 집중이 잘되게끔 하기 위함입니다.

 

position을 fixed로 줌으로써 페이지의 스크롤이나 움직임에 영향을 받지 않게 고정하고

 

실제 띄워지는 창을 화면 정중앙에 위치시키기 위해 display를 flex로 주고

 

축 방향과 반대 축 방향 모두 정렬을 center로 줍니다.

 

이제 이미지를 클릭해보면

 

이미지 모달

 

원래 보이던 페이지는 살짝 어두워지고 모달 창이 가운데 뜨는 것을 확인할 수 있습니다.

 

Modal.js

import { Component } from 'react';
import "./styles.css";

class Modal extends Component {
    constructor(props) {
        super(props);
        this.state = {
            index: this.props.index
        };
    }

    imageChange = (index) => {
        this.setState({
            index: index
        });
    }

    render() {
        const thumbnails = this.props.images.map((image, index) =>
            <img onClick={() => this.imageChange(index)} src={image} alt="alt" />
        );
        const currentImage = this.props.images[this.state.index];
        const length = this.props.images.length;

        return (
            <div className="background" onClick={() => this.props.close(null)}>
                <div className="modal" onClick={(e) => e.stopPropagation()}>
                    <div className="viewer">
                        <span onClick={() => this.imageChange((this.state.index + length - 1) % length)}>왼쪽으로</span>
                        <img src={currentImage} />
                        <span onClick={() => this.imageChange((this.state.index + 1) % length)}>오른쪽으로</span>
                    </div>
                    <div className="thumbnails">
                        {thumbnails}
                    </div>
                </div>
            </div>
        );
    }
}

export default Modal;

 

모달 컴포넌트에 코드 몇 줄을 추가하였습니다.

 

썸네일을 만들어 원하는 이미지를 모달 내에서 바로 볼 수 있도록 하였고

 

왼쪽 또는 오른쪽으로 이동도 가능하게끔 하였습니다.

 

그리고 props로 받아왔던 close 함수를 background에 붙여줬습니다.

 

e.stopPropagation() 이라는 함수는 부모 객체에서 onClick 함수를 더 이상 상속받지 않도록 해주는 함수입니다.

 

모달 창을 클릭해서 모달 창이 꺼지면 안 되기 때문에, 필수적으로 적어둬야 합니다.

 

물론 모달 내부에 닫기 버튼을 만들어 해당 버튼에 close 함수를 달아줘도 됩니다.

 

styles.css

.background {
    position: fixed;
    display: flex;
    align-items: center;
    justify-content: center;
    top:0;
    left:0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
}

.modal {
    background-color: white;
    width: 80%;
    height: 90%;
}

.viewer {
    text-align: center;
    height: 90%;
}

.viewer img {
    height: 100%;
}

.thumbnails {
    text-align: center;
    height: 10%;
}

.thumbnails img {
    height: 100%;
}

 

완성된 이미지 모달 기능

 

완성된 모습입니다.

 

css는 최소화하여 기능만을 구현했기 때문에 다소 썰렁할 수 있습니다..