Onde a entrada cai (corpo, atributo, dentro de script) decide o que o payload precisa fazer para virar código.
O mesmo valor refletido se comporta de formas diferentes conforme o lugar da página onde ele cai. Esse lugar é o contexto de saída, e cada um exige um payload diferente e um escape diferente.
Corpo do HTML: entre tags, como em <div>AQUI</div>. Basta abrir uma tag nova: <img src=x onerror=...>.
Dentro de um atributo: como em <input value="AQUI">. Primeiro você fecha a aspas e a tag (">), e só então injeta: "><img src=x onerror=...>. Às vezes dá para ficar no atributo: " autofocus onfocus="...".
Dentro de um <script>: o valor já está em contexto de código. Aqui você fecha a string e emenda comandos (";alert(1)//).
Numa URL (href, src): um javascript: pode virar código ao clique.
Saber o contexto é o primeiro passo: você olha o código-fonte, acha onde o seu valor apareceu, e escolhe o escape daquele contexto. A defesa segue a mesma lógica ao contrário: cada contexto tem a sua forma de codificar a saída.
O mesmo valor cai em três lugares; em cada contexto o payload precisa de um escape diferente para virar código.