본문 바로가기
카테고리 없음

TIL-16

by 오우지 2021. 10. 6.

Jinja2

 

전에 미세먼지 농도를 출력해줄 때 소숫점을 없애기 위해 {{ mise_gu|int }}를 설정해줬듯이

현재 오울봇이 보내주는 문자에 html 태그가 섞여 있기 때문에 안전하다고 얘기해줘야 html태그를 해독해준다.

따라서 

 

<span class="example">{{ definition.example|safe }}</span>

이렇게 써주면 <b></b>같은 볼드 태그가 잘 처리된다.

또한 이상한 특수문자의 제거를 위해

{{ definition.example.encode('ascii', 'ignore').decode('utf-8')|safe }}

ascii가 아니면 모두 무시하는 인코딩 해준다.

 

그 다음 검색 루트에 따라 보여줄 버튼을 설정하는데

url을 통해 status_receive를 받는다.

@app.route('/detail/<keyword>')
def detail(keyword):
    status_receive = request.args.get("status_give")
    # API에서 단어 뜻 찾아서 결과 보내기
    r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}"
                     , headers={"Authorization": "Token 0ee92a2de90415678ad90b8ee7abca13744282c8"})
    result = r.json()
    print(result)
    return render_template("detail.html", word=keyword, result=result, status=status_receive)

detail.html에서 

{% if status=="new" %}
                <button id="btn-save" class="btn btn-outline-sparta btn-lg">
                    <i class="fa fa-floppy-o" aria-hidden="true"></i>
                </button>
            {% else %}
                <button id="btn-delete" class="btn btn-sparta btn-lg">
                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                </button>
            {% endif %}

이렇게 설정해주면 이제 검색 루트에 따라 다른 버튼이 구현된다.

 

진자 템플릿을 통해 데이터를 보내줄 때 자바스크립트에서 변수를 받으면 따옴표를 따옴표로 받아주지 않는다.

데이터를 의도한대로 받을 수 없다는 의미이다.

let words = {{ words }};

따라서 다음과 같이 json데이터라고 명시해줘야 한다.

let words = {{ words|tojson }};

 

siblings() 객체는 item 객체와 동일한 위치에 있는 객체를 검색하는 메서드이다.

우리 코드에선 이렇게 적용해서

function find_word(){
            let word = $("#input-word").val().toLowerCase()
            if (word_list.includes(word)){
                //리스트에 있으면 하이라이트
                $(`#word-${word}`).addClass("highlight")
                $(`#word-${word}`).siblings().removeClass("highlight")
                $(`#word-${word}`)[0].scrollIntoView()
            }else{
                //리스트에 없으면 새 단어를 위한 상세페이지로
                window.location.href = `/detail/${word}?status_give=new`
            }
        }

각 tr에 대해 하나만 highlight라는 css 속성이 적용되게 설정했다.

 

<meta property="og:title" content="나만의 단어장"/>
<meta property="og:description" content="영어공부하쇼"/>
<meta property="og:image" content="{{ url_for('static', filename='logo_red.png') }}"/>
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">

og태그 설정을 위한 meta 값 입력


@MappedSuperclass

@MappedSuperclass//매핑 정보만 받는 수퍼 클래스
public class BaseEntity {

    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;

여러 테이블에 공통된 정보가 있을 떄 각각 추가하면 번거로움이 있으니까 공통부분은 MappedSuperclass를 이용해서 따로 묶어줄 수 있다

 

MappedSuperclass의 한계

-상속관계 매핑 X

-엔티티X, 테이블과 매핑X

-부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공

-조회, 검색 불가(em.find(BaseEntity)불가)

em.find(BaseEntity.class, member) 사용 불가

 

 

-직접 생성해서 사용할 일이 없으므로 추상 클래스 권장(이것만 해서는 할 수 있는게 없다)

-테이블과 관계 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할

-등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용

 

JPA에서 추상 클래스는 두 가지에만 쓰인다고 볼 수 있다. 하나는 Entity 또 다른 하나는 MappedSuperclass이다.

 

 

프록시

 

JPA에서는 em.find(), em.getReference() 두가지 조회가 존재한다.

em.find()는 데이터베이스를 통해서 실제 엔티티 객체를 조회

em.getReference()는 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회

 

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {

            Member member = new Member();
            member.setUsername("hello");

            em.persist(member);

            em.flush();
            em.clear();

            Member findMember = em.getReference(Member.class, member.getId());
            System.out.println("findMember.id = " + findMember.getId());
            System.out.println("findMember.username = " + findMember.getUsername());

            tx.commit();
        }catch (Exception e){
            tx.rollback();
        }finally {
            em.close();
        }


        emf.close();
    }
}

위의 코드는 getReference를 호출하는 시점과 id를 호출하는 시점에는 DB에 쿼리를 보내지 않지만 username을 호출하는 순간 username은 없기 때문에 쿼리를 쏴준다.

 

프록시 특징

-실제 클래스를 상속 받아서 만들어짐

-실제 클래스와 겉 모양이 같다.

-사용하는 입장에서 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.

-프록시 객체는 실제 객체의 참조를 보관

위와 같은 매커니즘으로 진행된다.

 

1. 프록시 객체는 처음 사용할 때 한 번만 초기화

2. 프록시 객체를 초기화 할 떄 프록시 객체가 실제 엔티티로 바뀌는 것은 아니다. 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능

3. 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함('=='비교 대신 instance of를 사용해야 한다)

4. 영속성 컨텍스트에 찾는 엔티티가 이미 있으면(다른 객체더라도) em.getReference()를 호출해도 실제 엔티티 반환

-이미 있는것이기 때문에 성능 면에서도 이득

-JPA는 ref를 먼저 실행하고 find를 실행해서 두개를 == 비교 해도 true를 보장해준다. 신기

5. 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때 프록시를 초기화하면 문제 발생

            Member member = new Member();
            member.setUsername("member1");

            em.persist(member);

            em.flush();
            em.clear();

            Member refMember = em.getReference(Member.class, member.getId());
            System.out.println("refMember = " + refMember.getClass());//proxy
            
            em.detach(refMember);
            
            refMember.getUsername();
            
            tx.commit();

이렇게 실행하면 영속성컨텍스트에서 관리가 안되기 때문에 오류가 발생한다.

 

프록시를 확인하는 방법

• 프록시 인스턴스의 초기화 여부 확인 PersistenceUnitUtil.isLoaded(Object entity)

• 프록시 클래스 확인 방법 entity.getClass().getName()

• 프록시 강제 초기화 org.hibernate.Hibernate.initialize(entity);

• 참고: JPA 표준은 강제 초기화 없음 강제 호출: member.getName()

 


오늘의 후기

웹 개발 진도가 너무 느리다. 중간에 재미가 없으면 자꾸 느려지는 것 같다. 내일 끝내는걸 목표로 아침부터 비는 시간 없이 빡 들어야겠다.

이번주는 시간이 좀 있어서 마지막에 스터디 진도를 들을 수 있었다. 본격적으로 프로젝트에 매달리기 전에 최대한 많은 공부를 해놔야겠다.