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()
오늘의 후기
웹 개발 진도가 너무 느리다. 중간에 재미가 없으면 자꾸 느려지는 것 같다. 내일 끝내는걸 목표로 아침부터 비는 시간 없이 빡 들어야겠다.
이번주는 시간이 좀 있어서 마지막에 스터디 진도를 들을 수 있었다. 본격적으로 프로젝트에 매달리기 전에 최대한 많은 공부를 해놔야겠다.