📌 Controlled Component

  • React 상태(state)를 통해 폼의 값을 제어
  • 폼 요소의 값이 항상 React 상태와 동기화됨
  • value와 onChange 이벤트를 사용해야 함
import { useState } from 'react';

function ControlledInput() {
    const [inputValue, setInputValue] = useState('');

    const handleChange = (e) => {
        setInputValue(e.target.value);
    };

    return (
        <input 
            type="text" 
            value={inputValue} 
            onChange={handleChange} 
        />
    );
}

 

 

장점

  • 폼의 값이 React 상태에 저장돼서 값을 추적하고 제어할 수 있음
  • 폼 데이터를 유효성 검사하거나 동적으로 수정할 때 적합

주의할 점

  • 항상 value와 onChange를 함께 사용해야 함
  • value에 undefined가 들어가면 오류가 발생할 수 있음
  • value={data || ''}처럼 기본값을 설정해줘야 함
<input value={undefined} /> // 오류 발생

 

 

📌 Uncontrolled Component

  • 폼 요소의 값을 DOM 자체에서 관리함
  • defaultValue 속성을 사용해 초기 값만 설정
  • 이후 값은 DOM에서 직접 관리되고, React 상태에 연결되지 않음
import { useRef } from 'react';

function UncontrolledInputWithRef() {
    const inputRef = useRef();

    const handleClick = () => {
        alert(inputRef.current.value);
    };

    return (
        <>
            <input ref={inputRef} type="text" defaultValue="Ref 사용 예시" />
            <button onClick={handleClick}>값 확인</button>
        </>
    );
}

 

장점

  • 구현이 간단하고, 상태 관리가 필요 없는 경우 사용하기 좋음
  • 폼 초기값만 필요할 때 적합

주의할 점

  • 값이 변경돼도 React에서는 해당 값을 추적할 수 없음
  • 값에 접근하려면 **Ref (useRef)**를 사용해야 함

 

📌 Controlled vs Uncontrolled Component 비교

구분 Controlled Component  Uncontrolled Component
상태 관리 React 상태(state)로 관리 DOM 자체에서 관리
초기 값 설정 value와 onChange 사용 defaultValue 사용
값 변경 추적 가능 (React 상태에 저장) 불가능 (Ref를 사용해야 함)
적합한 상황 값의 유효성 검사, 폼 제어 필요 시 간단한 초기값 설정 시, 상태 관리 불필요
주의할 점 항상 value와 onChange 함께 사용 초기값 이후 React에서 값 추적 불가

 

 

📌 사용 추천

  1. 폼 데이터가 중요하거나, 입력값을 추적해야 할 경우
    → Controlled Component 사용
  2. 초기 값만 설정하고, 이후 값 변경을 추적할 필요가 없는 경우
    → Uncontrolled Component 사용
  3. 간단한 폼이나 성능이 중요한 경우
    → Uncontrolled Component가 더 적합

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<style>
html, body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}
</style>
</head>
<body>
    <div class="modal" id="modal">
        <button class="exit" id="exit">&times;</button>
        <div id="google-map"></div>
    </div>
    
    <!-- Google Map API -->
    <script async defer
    src="https://maps.googleapis.com/maps/api/js?key={key}&callback=initMap">
    </script>
    <script>
        function initMap() {
            var map = new google.maps.Map(document.getElementById('google-map'), {
                zoom: 12.5,
                center: { lat: 37.4959854, lng: 127.0664091 }
            });

            var geocoder = new google.maps.Geocoder();

            var infoList = {"info": [
                {"name": "경기점", "addr": "경기 수원시 팔달구 덕영대로 924", "msg": "경기 유일 지점", "price": "90,000원", "img":"room1.jpg"},
                {"name": "서울역점", "addr": "서울 용산구 한강대로 405", "msg": "서울역 2분 거리", "price": "40,000원", "img":"room1.jpg"},
                {"name": "강남점", "addr": "서울 강남구 강남대로 지하 396", "msg": "전국 최대 규모", "price": "75,000원", "img":"room2.jpg"},
                {"name": "역삼점", "addr": "서울 강남구 테헤란로 지하 156", "msg": "고객 만족도 1위", "price": "110,000원", "img":"room2.jpg"}
            ]};

            for(var i in infoList.info) {
                geocodeAddress(geocoder, map, infoList.info[i]);
            }

            function geocodeAddress(geocoder, resultMap, info) {
                geocoder.geocode({'address': info.addr}, function(result, status) {
                    if (status === 'OK') {
                        resultMap.setCenter(result[0].geometry.location);
                        resultMap.setZoom(18);

                        var marker = new google.maps.Marker({
                            map: resultMap,
                            position: result[0].geometry.location
                        });
                        
                        var infowindow = new google.maps.InfoWindow();
                        var windowhtml = `
                            <div class='map_room_info'>
                                <div class='map_room_img'>
                                    <img src='http://localhost:9000/space/images/${info.img}'>
                                </div>
                                <div class='map_room_detail'>
                                    <p class='map_room_name'>${info.name}</p>
                                    <p class='map_room_msg'>${info.msg}</p>    
                                    <p class='map_room_addr'>${info.addr}</p>    
                                    <p class='map_room_price'>${info.price}
                                        <a href='http://localhost:9000/space/room_content.do'>상세 정보</a>
                                    </p>
                                </div>
                            </div>`;

                        google.maps.event.addListener(marker, 'click', function() {
                            infowindow.setContent(windowhtml);
                            infowindow.open(map, marker);
                        });

                        console.log('위도(latitude) : ' + marker.position.lat());
                        console.log('경도(longitude) : ' + marker.position.lng());
                    } else {
                        alert('지오코드가 다음의 이유로 성공하지 못했습니다 : ' + status);
                    }
                });
            }
        }
    </script>
</body>
</html>

'프론트엔드 > JavaScript' 카테고리의 다른 글

[JavaScript] Chart.js  (0) 2023.02.01
[JavaScript] url 파라미터 가져오기  (0) 2021.06.16
[JavaScript] Validation Check  (0) 2021.05.21
[JavaScript] JSON parse  (0) 2021.05.21
[JavaScript] JSON  (0) 2021.05.21

 

📌 1. TypeMismatchException: 매퍼 XML의 파라미터 불일치

 Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException 

 

원인

  • .xml 파일의 <insert> 태그에서 parameterType이 실제 전달된 객체와 일치하지 않음

해결 방법

  • 전달되는 객체의 패키지 및 클래스명을 정확하게 기입
  • VO 클래스의 필드와 MyBatis 매퍼 XML의 파라미터 타입 일치 확인
<insert id="insertOption" parameterType="com.myspace.vo.OptionVO">

 

 

📌 2. BeanCreationException: Mapper 위치 오류

 Error creating bean with name 'sqlSessionFactory' defined in ServletContext resource 

 

원인

  • mapperLocations 경로를 잘못 지정했을 때 발생
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath:com/myspace/mapper/*.xml" />
</bean>

 

 

📌 3. BeanCreationException: @RequestMapping 중복

 org.springframework.beans.factory.BeanCreationException: Injection of autowired dependencies failed
java.lang.IllegalStateException: Ambiguous mapping found 

 

원인

  • @RequestMapping에 동일한 URL을 사용하는 메서드가 여러 개 존재할 때 발생

해결 방법

  • @RequestMapping("/example")을 @GetMapping, @PostMapping 등으로 구분
  • 메서드별로 고유한 URL을 사용하여 중복을 피함

 

 

📌 4. SQLException: ORA-00907: missing right parenthesis

 bad SQL grammar []; nested exception is java.sql.SQLException: ORA-00907: missing right parenthesis 

 

원인:

  • SQL 쿼리에서 **괄호( )**나 **콤마(,)**가 누락되었을 때 발생

해결 방법:

  • 쿼리문을 다시 확인하여 괄호와 콤마의 짝을 맞춤
  • 특히 INSERT, SELECT 구문의 컬럼 및 값의 개수 일치 확인

 

 

 

 

 

 

 

 

 

 

 

git에 commit, push 할 때 변경되면 안되는 파일을 지정하는 방법

 

 

1. 파일 형식으로 지정

  • Preferences - Version Control - Ignored Resources

 

 

2. 파일 개별 지정

  • 파일 우클릭 - Team - Ignore

 

  • 파일 우클릭 - Team - Advanced - Assume Unchanged

 

 

  • Git Stagin - 파일 우클릭 - Assume Unchanged

 

 

<style>
    @media print {
    #header, #footer, .no_print { display: none; }
    }
</style>

// 프린트하지 않을 영역에 classname no_print 지정

 

function print() {  //새창에서 프린트
    var p = window.open(window.location.href, "_blank", "left=0, top=0, width=0, height=0");
    var style = "<link rel='stylesheet' href='http://localhost:9000/space/css/room.css'>"
        + "<style> ul{list-style-type: none; padding: 0; display: inline-block;} ul span{box-sizing: border-box;}</style>";

    p.document.write(style);	
    p.document.write($(".payment_wrap").html());
    p.document.close();
    p.focus();
    p.print();
    p.close();
} 


function print() {  //프린트 후 페이지 새로고침
    var init = $("body").html();

    window.onbeforeprint = function() {
        var print_area = $("#print_area").html();
        $("body").html(print_area);
        $("body").css({"max-width":"800px", "min-width":"800px"});
    };
    window.onafterprint = function() {
        location.reload();
    };
    window.print();
}

 

'프론트엔드 > JQuery' 카테고리의 다른 글

[JQuery] Ajax 답변삭제 팝업  (0) 2021.06.16
[JQuery] on 함수  (0) 2021.06.15
[JQuery] Ajax  (0) 2021.06.11
[JQuery] 부트스트랩 - Carousel(캐러셀) 만들기  (0) 2021.06.05
[JQuery] 클릭 이벤트  (0) 2021.05.25

+ Recent posts